题目相关
题目链接
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.
Find the number of ways to do this division. Two ways to do the division are considered different if and only if there is a position cut in only one of those ways.
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)。