動態規劃:求把數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);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章