整数划分 二 问题c语言,整数划分问题 动态规划

本文介绍了如何通过动态规划解决ACM/OI竞赛中的整数划分组合数问题,详细阐述了状态设计、状态转移过程,并提供了C++代码实现。讨论了拓展到不同条件下的划分计数,如按数量划分、不同数划分及奇数划分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

-------------------------------------------------------------------------------------

题目可见hduoj1028 , 简单的讲:

数字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分为两种状态,当前组合数等于这两种状态下的组合数之和。

n=1时,无论m等于多少,都只有一种{1}的划分,dp[1][m] = 1

m=1时,无论n等于多少,都只有一种{1,1,1,,,,,,}的划分,dp[n][1] = 1

n==m时,分两种情况,dp[n][m] = 1 + dp[n][m-1]:

划分数中包含m时,只有一种{m}

划分数中不包含m时,那最大值只能是m-1,为dp[n][m-1]

n>m时,m最大也只能取n,dp[n][m] = dp[n][n]

n

划分数中包含m时,最后一个划分数必须为m(前面也可以有m)sum{........m}=n,dp[n-m][m]

划分数中不包含m时,那最大划分数只能是m-1,dp[n][m-1]

递归比较方便,先放记忆化递归的代码:

#include

using namespacestd;const int MAXN = 128;int dp[MAXN][MAXN] = {0};int DP(int n, intm)

{//不合法情况

if (n <= 0 || m <= 0) return 0;//记忆化

if (dp[n][m] > 0) returndp[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;

}intmain()

{intn;while (cin >>n) {

cout<< DP(n, n) <

}return 0;

}

DP递推版

#include

using namespacestd;const int MAXN = 128;int dp[MAXN][MAXN] = { 0};void init(intmaxn)

{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

else if (n > m) dp[n][m] = dp[n - m][m] + dp[n][m - 1];

}

}

}intmain()

{

init(120);intn;while (cin >>n) {

cout<< dp[n][n] <

}return 0;

}

-------------------------------------------------------------------------------------

现在考虑一些更加复杂的情况

第一行: 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

using namespacestd;const int MAXN = 55;int dp1[MAXN][MAXN] = { 0};void init1(intmaxn)

{//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(intmaxn)

{

}intmain()

{

init1(50);intn, k;while (cin >> n >>k) {

cout<< dp1[n][k] <

}return 0;

}

-------------------------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值