整数划分问题的动态规划方案

本文详细解析了整数划分问题的动态规划解决方案,包括整数M划分为任意个整数的方案数量以及整数M划分为任意个不同整数的方案数量。通过实例演示了如何使用动态规划推导公式逐步计算出所有可能的划分方案。

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

整数划分问题:

(一)整数M划分为任意个整数的方案数量;

(二)整数M划分为任意个不同整数的方案数量。

动态规划:

动态规划是当前因子可以由前序若干个因子推导出来情况,根据推导公式来依次求出当前因子及后续因子的方法。关键在于找到当前因子与前序因子之间的固定推导公式。还需要找到一些边界值,来确定初始因子。

下面用动态规划的方法分析整数划分问题,我们用dp(N,M)表示整数M划分为N个整数的方案数量。dp(i,j)的前序因子为dp(1,1)、dp(1,2)、……、dp(1,j)、dp(2,1)、dp(2,2)、……、dp(2,j)、……、dp(i-1,j)、dp(i,j-1)。

(一)整数M划分为任意个整数(可重复)

假设dp(i,j)有下面所列的划分方案:

1,xxxxxx

1,xxxxxx

......

1,xxxxxx

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

2,xxxxxx

2,xxxxxx

......

x,xxxxxx

横线以上为包含数字1的划分方案情况,横线下方为不包含数字1的划分情况。包含1的情况,如果去掉组合中的第一个1,即dp(i-1,j-1)。

不包含1的情况,组合中的最小数为2,给每个方案的每一位数字减1,即dp(i,j-i);

由此可确定推导公式:dp(i,j)=dp(i-1,j-1)+dp(i,j-i);

再根据常识推导出一些边界值的公式:

dp(0,0)=1;   dp(0,j)=0(j>0);    dp(1,j)=1(j>0);    dp(j,j)=1;    dp(i,j)=0(i<j);       dp(i,j)=1(i=j-1)。

第一个边界值dp(0,0)可以推导出后续的所有因子,而后面的边界值可以在程序中辅助减少计算的时间。

例如M=10,开始从dp(1,1)依次向后推导,可以将每个推导结果存在二维数组中,方便推导后续节点:

 j=0j=1j=2j=3j=4j=5j=6j=7j=8j=9j=10
i=010000000000
i=101111111111
i=2001

dp(2,3)=

dp(1,2)+

dp(2,1)

=1+0=1

dp(2,4)=

dp(1,3)+

dp(2,2)

=1+1=2

dp(2,5)=

dp(1,4)+

dp(2,3)

=1+1=2

dp(2,6)

=dp(1,5)+

dp(2,4)

=1+2=3

dp(2,7)=

dp(1,6)+

dp(2,5)

=1+2=3

dp(2,8)=

dp(1,7)+

dp(2,6)

=1+3=4

dp(2,9)=

dp(1,8)+

dp(2,7)

=1+3=4

dp(2,10)=

dp(1,9)+

dp(2,8)

=1+4=5

i=30001

dp(3,4)=

dp(2,3)+

dp(3,1)

=1+0=1

dp(3,5)=

dp(2,4)+

dp(3,2)

=2+0=2

dp(3,6)=

dp(2,5)+

dp(3,3)

=2+1=3

dp(3,7)=

dp(2,6)+

dp(3,4)

=3+1=4

dp(3,8)=

dp(2,7)+

dp(3,5)

=3+2=5

dp(3,9)=

dp(2,8)+

dp(3,6)

=4+3=7

dp(3,10)=

dp(2,9)+

dp(3,7)

=4+4=8

i=400001

dp(4,5)=

dp(3,4)+

dp(4,1)

=1+0=1

dp(4,6)=

dp(3,5)+

dp(4,2)

=2+0=2

dp(4,7)=

dp(3,6)+

dp(4,3)

=3+0=3

dp(4,8)=

dp(3,7)+

dp(4,4)

=4+1=5

dp(4,9)=

dp(3,8)+

dp(4,5)

=5+1=6

dp(4,10)=

dp(3,9)+

dp(4,6)

=7+2=9

i=5000001

dp(5,6)=

dp(4,5)+

dp(5,1)

=1+0=1

dp(5,7)=

dp(4,6)+

dp(5,2)

=2+0=2

dp(5,8)=

dp(4,7)+

dp(5,3)

=3+0=3

dp(5,9)=

dp(4,8)+

dp(5,4)

=5+0=5

dp(5,10)=

dp(4,9)+

dp(5,5)

=6+1=7

i=60000001

dp(6,7)=

dp(5,6)+

dp(6,1)

=1+0=1

dp(6,8)=

dp(5,7)+

dp(6,2)

=2+0=2

dp(6,9)=

dp(5,8)+

dp(6,3)

=3+0=3

dp(6,10)=

dp(5,9)+

dp(6,4)

=5+0=5

i=700000001

dp(7,8)=

dp(6,7)+

dp(7,1)

=1+0=1

dp(7,9)=

dp(6,8)+

dp(7,2)

=2+0=2

dp(7,10)=

dp(6,9)+

dp(7,3)

=3+0=3

i=8000000001

dp(8,9)=

dp(7,8)+

dp(8,1)

=1+0=1

dp(8,10)=

dp(7,9)+

dp(8,2)

=2+0=2

i=90000000001

dp(9,10)=

dp(8,9)+

dp(9,1)

=1+0=1

i=1000000000001

(二)整数划分为不同的整数之和。

还是上面的方案例子,不同之处在于横线上方的方案去掉1之后,后面的整数>=1,是dp(i-1,j-1)不包括1的所有方案,需要减去以1开头的所有方案数量,即dp(i-1,j-1)-dp(i-1,j-1)(方案包含1)。横线下方的方案数量仍然是dp(i,j-i)。

横线上方的方案,去掉1之后,后面的i-1个数字都大于1,让每个数字-1后,即dp(i-1)(j-1-(i-1))=dp(i-1)(j-i)。

所以整数划分为不同整数之和的动态推导公式为:dp(i,j)=dp(i-1)(j-i) + dp(i,j-i)

1,xxxxxx

1,xxxxxx

......

1,xxxxxx

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

2,xxxxxx

2,xxxxxx

......

x,xxxxxx

例如M=10

动态推导公式:dp(i,j)=dp(i-1)(j-i) + dp(i,j-i)

边界值:

dp(0,0)=1;    dp(1,j)=1;    dp(1,j)=0(j>0);    dp(i,j)=0(j<(i*(i+1)/2)),dp(i,j)=1(j=(i*(i+1)/2)),(因为i个不同数的最小值为1+2+3+...+(i-1)+i的等差数列,和为i*(i+1)/2)。

下表直接写0,1标红的都是根据边界值计算的,其他用动态推导公式推导。用这个表可以看出所有的值都可以通过边界值公式和动态推导公式获得。

表后为c的推导程序,程序中只用了第一个边界值dp(0,0)=1,其他都用推导的方式获得。

 j=0j=1j=2j=3j=4j=5j=6j=7j=8j=9j=10
i=010000000000
i=101111111111
i=20001

dp(2,4)=

dp(1,2)+dp(2,2)

=1+0=1

dp(2,5)=

dp(1,3)+dp(2,3)

=1+1=2

dp(2,6)=

dp(1,4)+dp(2,4)

=1+1=2

dp(2,7)=

dp(1,5)+dp(2,5)

=1+2=3

dp(2,8)=

dp(1,6)+dp(2,6)

=1+2=3

dp(2,9)=

dp(1,7)+dp(2,7)

=1+3=4

dp(2,10)

=dp(1,8)+dp(2,8)

=1+3=4

i=30000001

dp(3,7)=

dp(2,4)+dp(3,4)

=1+0=1

dp(3,8)=

dp(2,5)+dp(3,5)

=2+0=2

dp(3,9)=

dp(2,6)+dp(3,6)

=2+1=3

dp(3,10)=

dp(2,7)+dp(3,7)

=3+1=4

i=400000000001
i=500000000000
i=600000000000
i=700000000000
i=800000000000
i=900000000000
i=1000000000000

 

 

 

 

 

 

 

 

 

 

int main(){
	int m=0;
	int i=0, j=0;
    printf("输入M:");
	scanf("%d", &m);

	int dp[30][30] = {0};

	dp[0][0] = 1;
	for (i=1; i<=m; i++)
	{
		for (j=i*(i+1)/2; j<=m; j++)
		{
			dp[i][j]=dp[i][j-i]+dp[i-1][j-i];
		}
	}

	for (i=0; i<=m; i++)
	{
		printf("%d: ",i);
		for (j=0; j<=m; j++)
		{
			printf("%d, ", dp[i][j]);
		}
		printf("\n");
	}
	int ans = 0;
	for (i=1;i<=m;i++)
	{
		ans+=dp[i][m];
	}
	printf("answer : %d\n", ans);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值