AtCoder題解 —— AtCoder Beginner Contest 185 —— C - Duodecim Ferra —— 組合數學、揹包
技術標籤:OJ題解# AtCoder題解AtCoder題解ABC185C題Duodecim Ferra組合數學
題目相關
題目連結
AtCoder Regular Contest 185 C 題,https://atcoder.jp/contests/abc185/tasks/abc185_c。
Problem Statement
There is an iron bar of length L lying east-west. We will cut this bar at 11 positions to divide it into 12 bars. Here, each of the 12 resulting bars must have a positive integer length.
Under the constraints of this problem, it can be proved that the answer is less than .
Input
Input is given from Standard Input in the following format:
L
Output
Print the number of ways to do the division.
Samples1
Sample Input 1
12
Sample Output 1
1
Explaination
There is only one way: to cut the bar into 12 bars of length 1 each.
Samples2
Sample Input 2
13
Sample Output 2
12
Explaination
Just one of the resulting bars will be of length 2. We have 12 options: one where the westmost bar is of length 2, one where the second bar from the west is of length 2, and so on.
Samples3
Sample Input 3
17
Sample Output 3
4368
Constraints
- 12≤L≤200
- L is an integer.
題解報告
題目翻譯
一個長度為 L 的鐵棍,我們將切 11 次,將鐵棍分為 12 個部分,每個部分的長度為正整數。請找出一共有幾種切法。在本問題中,保證答案是小於 。
題目分析
又是一個數學題。通過不同的角度看問題,我們有不同解法。已知長度為 L,切 11 次。裁點有 L-1 個,我們取其中的 11 個,根據組合答案為 ,這樣問題就變成如何解組合數。
資料範圍估計
L 最大為 200,因此最大的資料為 ,需要使用 long long。
遞推
根據組合數學,我們有這樣的遞推關係:。
AC 參考程式碼
//https://atcoder.jp/contests/abc185/tasks/abc185_c
//C - Duodecim Ferra
#include <bits/stdc++.h>
using namespace std;
//如果提交到OJ,不要定義 __LOCAL
#define __LOCAL
typedef long long LL;
const int MAXL=2e2+4;
LL ans[MAXL][MAXL];
int main() {
#ifndef __LOCAL
//這部分程式碼需要提交到OJ,本地除錯不使用
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#endif
LL l;
cin>>l;
//初始化
l--;
for (int i=0; i<=l; i++) {
ans[i][0]=1;
ans[i][i]=1;
ans[i][1]=i;
}
//遞推計算
for (int i=1; i<=l; i++) {
for (int j=2; j<i; j++) {
ans[i][j]=ans[i-1][j-1]+ans[i-1][j];
}
}
cout<<ans[l][11]<<"\n";
#ifdef __LOCAL
//這部分程式碼不需要提交到OJ,本地除錯使用
system("pause");
#endif
return 0;
}
時間複雜度
O(L^2)。
空間複雜度
O(L^2)。
改進
通過遞推,我們得到一個 O(L^2) 時間複雜度的演算法。下面我們來討論一下,如何優化演算法。我們還是從數學入手。根據組合定義,我們可以寫出如下計算:
。
這樣我們可以得到以下的改善程式碼。
AC 參考程式碼
//https://atcoder.jp/contests/abc185/tasks/abc185_c
//C - Duodecim Ferra
#include <bits/stdc++.h>
using namespace std;
//如果提交到OJ,不要定義 __LOCAL
//#define __LOCAL
int main() {
#ifndef __LOCAL
//這部分程式碼需要提交到OJ,本地除錯不使用
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#endif
long long l;
cin>>l;
long long ans=1;
for (long long i=1; i<=11; i++) {
ans *= (l-i);
ans /=i;
}
cout<<ans<<"\n";
#ifdef __LOCAL
//這部分程式碼不需要提交到OJ,本地除錯使用
system("pause");
#endif
return 0;
}
時間複雜度
O(1)。
空間複雜度
O(1)。
這樣,我們通過數學優化,將 O(L^2) 時間複雜度優化到 O(1)。
揹包
同樣我們可以用揹包思路。假設 f[i][j] 表示用 i 個正整數湊出 j 的方案數,k 表示長度。遞推的關係為:
AC 參考程式碼
//https://atcoder.jp/contests/abc185/tasks/abc185_c
//C - Duodecim Ferra
#include <bits/stdc++.h>
using namespace std;
//如果提交到OJ,不要定義 __LOCAL
//#define __LOCAL
typedef long long LL;
const int MAXL=2e2+2;
LL ans[12+2][MAXL];
int main() {
#ifndef __LOCAL
//這部分程式碼需要提交到OJ,本地除錯不使用
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#endif
LL l;
cin>>l;
//初始化
ans[0][0]=1;
//遞推
for (LL i=1; i<=12; i++) {
for (LL k=1; k<=l; k++) {
for (LL j=l; j>=max(k, i); j--) {
ans[i][j] += ans[i-1][j-k];
}
}
}
cout<<ans[12][l]<<"\n";
#ifdef __LOCAL
//這部分程式碼不需要提交到OJ,本地除錯使用
system("pause");
#endif
return 0;
}
時間複雜度
O(L^2)。
空間複雜度
O(L)。