动态规划:求把数n分成若干个正数相加的总情况数

题目:

N=a[1]+a[2]+a[3]…+a[m];
a[i]>0,1<=m<=N;
对于一个正整数,求解满足上面公式的所有算式组合,如对于整数4:
4=4
4=3+1
4=2+2
4=2+1+1
4=1+1+1+1
所以上面的结果是5
注意对于“4=3+1”和“4=1+3”,这两处算式实际上是一个组合!
输入
每个用例中,会有多行输入,每行输入一个正整数,表示要求解的正整数N(1<=N<=120)
输出:
对输入中的每个整数求解答案,并输出一行(回车换行)
样例:
4
10
20
输出:
5
42
627

题目分析:

首先把问题分解为: 将数n分解成m个非零项相加(只考虑非零,因为4=4和4=4+0是一样的,后面再把m从1到n遍历即可得出完整答案),设有f[n][m]种结果,可分为两种情况简化问题:
情况①: 如果这m个项中含有1,那么等式两边同时减去1(如4=1+1+2变成3=1+2),
那么这种情况就成为一个子问题: “把数n-1分解成m-1个单项式”,而其结果为f[n-1][m-1]

情况②: 如果这m个项中不包含1(即每项都≥2),那么对于符合该条件的每一种情况 ,如果我对右式每项都-1. 相应的为保持等式成立,数n也要-m。而这样操作后与操作前的情况数是不变的。
换句话说就是:情况②的数目与“把数n-m分解成m个单项式:f[n-m][m]”是一致的

所以我们分解化简后转化出这样两个式子:
多项式中含有1时,对应的结果为 f[n-1][m-1]
多项式中不含有1时,对应的结果为 f[n-m][m]

两情况是对立的,所以总结果数为两者相加:f[n][m]=f[n-1][m-1]+f[n-m][m]
所以我们得到了一个递推关系式。如果我们能得出 f[n-1][m-1]和 f[n-m][m]的正确结果,那么就能得出 f[n][m]的正确结果

然而前两者的正确结果该如何计算呢?其实跟我们目前的问题解法是完全一致的,只是参数不同而已。
所以需要一直递归地分解问题,直到情况不断分解至我们能控制的范围(n=1)为止。

然而递归只是一种思路,比较好理解,在编写程序的时候递归函数会占用大量资源,不建议使用。

#include <stdio.h>
int main()
{
	int f[121][121]={0}; //f[0][0]可读性差,直接从f[1][1]开始,所以多定义一行空间
	//把所有值初始化为0,因为递归公式有可能访问到无意义的值,
	//如f[1][3](把数1分成3个正整数单项式,是不存在情况的),如果不赋初值f[1][3]将会是随机数。 
	int maxNum = 120;
	
	//赋值根本依据 
	for(int i=0;i<=maxNum;i++)
	{
		f[i][1] = 1;//拆成1项的情况:只有1种 
	} 
	
	//逐层算出结果 
    for(int i = 1; i<=maxNum; i++)//数n为i时的情况。 
	{
        for(int j = 2; j<=i; j++)//计算拆成j项的情况 ,i逻辑上≥j(由题意项数不可能大于数n)。 
		{
            f[i][j] = f[i-1][j-1] + f[i-j][j];
        }
    }
    
    //输出答案 
    int n,answer;
	while(~scanf("%d",&n))
	{
		answer=0; 
		for(int j=1;j<=n;j++)//最终结果是 把数分成1~n项的情况数 的和 
		{
			answer+=f[n][j];
		}
		printf("%d\n",answer);
	}
}
原创文章 6 获赞 10 访问量 1387

猜你喜欢

转载自blog.csdn.net/mkr67n/article/details/104139460