题目来源:http://noi.openjudge.cn/ch0207/7219/
7219:复杂的整数划分问题
总时间限制: 200ms 内存限制: 65536kB
描述
将正整数n 表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 ,k>=1 。
正整数n 的这种表示称为正整数n 的划分。
输入
标准的输入包含若干组测试数据。每组测试数据是一行输入数据,包括两个整数N 和 K。
(0 < N <= 50, 0 < K <= N)
输出
对于每组测试数据,输出以下三行数据:
第一行: N划分成K个正整数之和的划分数目
第二行: N划分成若干个不同正整数之和的划分数目
第三行: N划分成若干个奇正整数之和的划分数目
样例输入
5 2
样例输出
2
3
3
提示
第一行:4+1, 3+2,
第二行: 5,4+1,3+2
第三行: 5,1+1+3, 1+1+1+1+1+1
-----------------------------------------------------
解题思路
动态规划
1. k个正整数和
dp[i][j]: i的j个正整数划分数, dp[i][j] =dp[i-j][j]+dp[i-1][j-1]
dp[i-j][j]: i-j的j个正整数划分上每个正整数+1;
dp[i-1][j-1]: i-1的j-1个正整数划分再添上一个1
// 超级巧妙的思路哇~
2. 不同整数和
dp[i][j](1<=j<=i): 整数i的这样的划分数,其中每个划分得到的最大整数为j
dp[1][1] = 1
dp[i][j] =sum(dp[i-j][k]), 2<=k<j, whenj<i
dp[i][j] = 1, when i==j
3. 正奇数和
dp[i][j]: 正整数i的这样的划分数,使得每个划分中最大的正奇数为j
dp[i][j] =dp[i-j][k], 1<=k<=j, k是正奇数
// 2. 和 3. 都是在NOI 2.7 7215:简单的整数划分问题的基础上的一点小改动
// 2. 就是求和的时候k必须小于j,不能等于j,且k要从2开始遍历
// 3. 就是j、k都是奇数
-----------------------------------------------------
代码
// 动态规划 // 1. k个正整数和 // dp[i][j]: i的j个正整数划分数, dp[i][j] = dp[i-j][j]+dp[i-1][j-1] // dp[i-j][j]: i-j的j个正整数划分上每个正整数+1; // dp[i-1][j-1]: i-1的j-1个正整数划分再添上一个1 // 2. 不同整数和 // dp[i][j] (1<=j<=i): 整数i的这样的划分数,其中每个划分得到的最大整数为j // dp[1][1] = 1 // dp[i][j] = sum(dp[i-j][k]), 2<=k<j, when j<i // dp[i][j] = 1, when i==j // 3. 正奇数和 // dp[i][j]: 正整数i的这样的划分数,使得每个划分中最大的正奇数为j // dp[i][j] = dp[i-j][k], 1<=k<=j, k是正奇数 #include<iostream> #include<cstring> using namespace std; const int NMAX = 55; int dp[NMAX][NMAX] = {}; void clearDP() // 清零dp数组 { int i; for (i=0; i<NMAX; i++) { memset(dp[i], 0, sizeof(dp[i])); } return; } int max_odd(int i) //返回小于等于i的最大正奇数 { if (i%2==1) { return i; } else { return (i-1); } } int main() { int n,k,i1,i2,i3,ans2,ans3; while (cin >> n >> k) { if (n==1) { cout << 1 << endl << 1 << endl << 1 << endl; continue; } ans2 = 0; ans3 = 0; // 1. k个正整数和 clearDP(); dp[1][1] = 1; for (i1=2; i1<=n; i1++) { for (i2=1; i2<=k; i2++) { if (i2<=i1) { dp[i1][i2] = dp[i1-i2][i2] + dp[i1-1][i2-1]; } } } cout << dp[n][k] << endl; // 2. 不同的正整数和 clearDP(); dp[1][1] = 1; for (i1 = 2; i1 <= n; i1++) { for (i2 = 2; i2<i1; i2++) { for (i3 = 1; i3 < i2; i3++) { dp[i1][i2] += dp[i1-i2][i3]; } } dp[i1][i1] = 1; } for (i1=1; i1<=n; i1++) { ans2 += dp[n][i1]; } cout << ans2 << endl; // 3. 正奇数和 clearDP(); dp[1][1] = 1; for (i1=2; i1<=n; i1++) { for (i2=1; i2<=max_odd(i1); i2=i2+2) { for (i3=1; i3<=i2; i3=i3+2) { dp[i1][i2] += dp[i1-i2][i3]; } } if (i1==max_odd(i1)) { dp[i1][i1] = 1; } } for (i1=0; i1<=n; i1++) { ans3 += dp[n][i1]; } cout << ans3 << endl; } return 0; }