整数划分问题动态规划

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

整数划分问题动态规划
ACM,OI等⽐赛,整数划分为常见的⼊门题,许久没打⽐赛,最近做笔试题突然碰到,磕磕绊绊了很久才搞清楚,现在做个笔记。

-------------------------------------------------------------------------------------
题⽬可见 , 简单的讲:
数字N,整数划分的组合数为多少。

整数划分表⽰正整数的和集为N,⽐如N=4时:
4 = 4;
4 = 3 + 1;
4 = 2 + 2;
4 = 2 + 1 + 1;
4 = 1 + 1 + 1 + 1;
有这5中情况,所以N=4的整数划分的组合数为5。

-------------------------------------------------------------------------------------
解题,开始DP,先对DP进⾏状态的设计:
思考的第⼀步:考虑到是组合,不能出现重复的情况,⽐如4=3+1与4=1+3是⼀种,所以采⽤“划分数中的最⼤值”来限定重复状态。

思考的第⼆步:状态设计:dp[n][n]表⽰“和为n、最⼤划分数为m”时,组合的数量。

思考的第三步:状态转移:由划分数中是否包含m分为两种状态,当前组合数等于这两种状态下的组合数之和。

1. n=1时,⽆论m等于多少,都只有⼀种{1}的划分,dp[1][m] = 1
2. m=1时,⽆论n等于多少,都只有⼀种{1,1,1,,,,,,}的划分,dp[n][1] = 1
3. n==m时,分两种情况,dp[n][m] = 1 + dp[n][m-1]:
1. 划分数中包含m时,只有⼀种{m}
2. 划分数中不包含m时,那最⼤值只能是m-1,为dp[n][m-1]
4. n>m时,m最⼤也只能取n,dp[n][m] = dp[n][n]
5. n<m时,分两种情况,dp[n][m] = dp[n-m][m] + dp[n][m-1]:
1. 划分数中包含m时,最后⼀个划分数必须为m(前⾯也可以有m)sum{........m}=n,dp[n-m][m]
2. 划分数中不包含m时,那最⼤划分数只能是m-1,dp[n][m-1]
递归⽐较⽅便,先放记忆化递归的代码:
#include<iostream>
using namespace std;
const int MAXN = 128;
int dp[MAXN][MAXN] = {0};
int DP(int n, int m)
{
// 不合法情况
if (n <= 0 || m <= 0) return0;
// 记忆化
if (dp[n][m] > 0) return dp[n][m];
// 1. n=1只有⼀种{1};
// 2. m=1只有⼀种{1,1,1,,,,,,}
if (n == 1 || m == 1) return dp[n][m] = 1;
// 1. 包含m只有⼀种{m}
// 2. 不包含m, dp[n][m-1]
// n==m,这两种情况为所有情况
if (n == m) return dp[n][m] = 1 + DP(n, m - 1);
// m最⼤也只能是n
if (n < m) return dp[n][m] = DP(n, n);
// 1. 包含m, 将最后⼀个数定为m, dp[n-m][m]
// 2. 不包含m, 最⼤的数为m-1, do[n][m-1]
// n>m,这两种情况为所有情况
if (n > m) return dp[n][m] = DP(n - m, m) + DP(n, m - 1);
// 不合法情况
return dp[n][m] = 0;
}
int main()
{
int n;
while (cin >> n) {
cout << DP(n, n) << endl;
}
return0;
}
DP递推版
#include<iostream>
using namespace std;
const int MAXN = 128;
int dp[MAXN][MAXN] = { 0 };
void init(int maxn)
{
for (int n = 1; n <= maxn; n++) {
for (int m = 1; m <= maxn; m++) {
// n=1时,⽆论m等于多少,都只有⼀种{1}的划分,dp[1][m] = 1
// m = 1时,⽆论n等于多少,都只有⼀种{ 1,1,1,,,,,, }的划分,dp[n][1] = 1
if (n == 1 || m == 1) dp[n][m] = 1;
// n==m时,分两种情况,dp[n][m] = 1 + dp[n][m-1]:
// 1. 划分数中包含m时,只有⼀种{ m }
// 2. 划分数中不包含m时,那最⼤值只能是m - 1,为dp[n][m - 1]
else if (n == m) dp[n][m] = 1 + dp[n][m-1];
// n>m时,m最⼤也只能取n,dp[n][m] = dp[n][n]
else if (n < m) dp[n][m] = dp[n][n];
// n<m时,分两种情况,dp[n][m] = dp[n-m][m] + dp[n][m-1]:
// 1. 划分数中包含m时,最后⼀个划分数必须为m(前⾯也可以有m)sum{........m}=n,dp[n-m][m] // 2. 划分数中不包含m时,那最⼤划分数只能是m-1,dp[n][m-1]
else if (n > m) dp[n][m] = dp[n - m][m] + dp[n][m - 1];
}
}
}
int main()
{
init(120);
int n;
while (cin >> n) {
cout << dp[n][n] << endl;
}
return0;
}
-------------------------------------------------------------------------------------
现在考虑⼀些更加复杂的情况
第⼀⾏: N划分成K个正整数之和的划分数⽬
第⼆⾏: N划分成若⼲个不同正整数之和的划分数⽬
第三⾏: N划分成若⼲个奇正整数之和的划分数⽬
具体举例:对于N=5,K=2
第⼀⾏: 4+1, 3+2,
第⼆⾏: 5,4+1,3+2
第三⾏: 5,1+1+3, 1+1+1+1+1+1
#include<iostream>
using namespace std;
const int MAXN = 55;
int dp1[MAXN][MAXN] = { 0 };
void init1(int maxn)
{
// DP状态设计,dp[n][k]表⽰数字n,被划分成k个数的组合情况数,初始化为0
// 定下了K个数,那么以这K个数中包不包括1分为两个情况,这样都能做状态转移
for (int n = 1; n <= maxn; n++) {
for (int k = 1; k <= maxn; k++) {
// 1. k=1时只有⼀种,{n}
// 2. k=n时只有⼀种,{1,1,1,,,,,,}
if (k == 1 || k == n) dp1[n][k] = 1;
// 1. 这k个数字中没有1, dp[n-k][k]为每个数字减1的情况数
// 2. 这k个数字中有1, dp[n-1][k-1]该情况下加上数字1
if (n > k) dp1[n][k] = dp1[n - k][k] + dp1[n - 1][k - 1];
// n < k 为0
}
}
}
int init2(int maxn)
{
}
int main()
{
init1(50);
int n, k;
while (cin >> n >> k) {
cout << dp1[n][k] << endl;
}
return0;
}
-------------------------------------------------------------------------------------。

相关文档
最新文档