將正整數n表示為一系列正整數之和,n=n1+n2+n3+n4+......+nk (其中,n1>=n2>=n3>=n4........>=nk>0,k>=1)
正整數n的這種表示成為正整數n的劃分。正整數n的不同劃分個數成為正整數n的劃分數,記作p(n)。
例如,正整數6有如下11種劃分:
6;5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1;
其實這種問題可以認為是把n劃分為 加數小於或等於某個數的劃分,在這裡把這個數設為m。例如,對6的劃分可以認作是將6劃分為加數小於等於6的劃分,因為6的加數確實小於等於6,為什麼要引入這個m呢,是因為我們發現,從這個角度思考,比較容易求解。我們將劃分的種類數記為q(n,m),表示n劃分為小於或等於m的數的和的劃分數。
在遞迴過程中,要有遞迴出口,所以下面是對遞迴引數的處理。
1.n==1,時,顯然 q(n,m)=1;
2.m==1時,顯然 q(n,m)=1;
3.n,因為在n的劃分數均小於或等於n,所以q(n,m)=q(n,n);
4.n==m時,即q(n,n);
此時就是對n的劃分出來的數沒有限制,預設限制就是不大於n,此時劃分的總類數要分兩種情況才比較好解決:
1.劃分出來的數包含n(或m,因為n==m):那只有一種方式 比如 6的劃分 只有 6;一種方式
2.劃分出來的數不包含n(或m,因為n==m):就可以認為是將6劃分出來的數都小於6,其實就是都小於或等於5,接下來 其實就是求出來q(n,n-1)或者是q(n,m-1),此時n>m-1,放到遞迴方程裡就是求解q(n,m) n>m
綜合起來 q(n,n)=1+q(n,m-1);
5.n>m時,
當遇到這個問題時,其實可以看做是對n的劃分有了條件,就是所有的劃分出來的數小於m,在上文中,6有11種劃分方式,那是沒有對6劃分出來的數進行限制,當要使劃分出來的數都小於某個數時比如5時,那就不是11種了。
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1;
這個時候分兩種情況:包含m和不包含m :
不包含m就是:
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1;這些情況,其實就是求6的劃分出來的數小於等於4的情況,放到遞迴方程就是 q(n,m-1)
包含m就是:
5+1;
這個時候,確定這種劃分數量的時候m不再是主角,我們只要求出來n-m的劃分情況就行了,因為此時的m的劃分情況,取決於n-m。 比如包含m=5的劃分情況就是 6-5=1的情況,比如包含4的劃分情況就是求6-4=2,2的沒有限制的劃分情況。放到遞迴方程裡面就是q(n-m,m)。
綜上:
q( n, n ) = 1, 當 n ==1;
q( n, n ) = 1, 當m == 1;
q( n, n ) = q( n,n ) , 當n < m;
q( n, n ) = q( n, m-1) +1 , 當n == m;
q( n, m ) = q( n - m, m ) + q( n , m -1) , 當n > m;
思路:來自
#include#include#include#include#include#include#include#include#include#include#include#include#define cla(a, sum) memset(a, sum, sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int q(int n,int m)
else if(n>m)else if(n下面來講一下dp做法,其實理解了上面的遞迴思路,dp做法就很容易了,dp做法說白了就是狀態轉移,量的轉移。
dp[n][m]表示「和為n、最大劃分數為m」時,組合的數量。
根據上面的思路可以寫出下面的dp式:
n=1時,無論m等於多少,都只有一種的劃分,dp[1][m] = 1
m=1時,無論n等於多少,都只有一種的劃分,dp[n][1] = 1
n==m時,分兩種情況,dp[n][m] = 1 + dp[n][m-1]:
劃分數中包含m時,只有一種
劃分數中不包含m時,那最大值只能是m-1,為dp[n][m-1]
nn>m時,分兩種情況,dp[n][m] = dp[n-m][m] + dp[n][m-1]:
劃分數中包含m時,最後乙個劃分數必須為m(前面也可以有m)sum=n,dp[n-m][m]
2. 劃分數中不包含m時,那最大劃分數只能是m-1,dp[n][m-1]
附上**:也是 這個題目的題解
#include#include#include#include#include#include#include#include#include#include#include#define cla(a, sum) memset(a, sum, sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=3e4;
int n;
int dp[125][125];
void init()//打表預處理 }}
int main()
} }for(i=1;i<=50;i++)//dp2[i][j]把i劃分成最大數為j的不同的正整數的種類數 }
//dp3[i][j]把i劃分成最大數為j的正奇數的種類數
for(i=1;i<=50;i++)
else dp3[i][j]=dp3[i][j-1];
//偶數時,i的劃分中不能包含j。
}} }
}int main()
return 0;
}
整數劃分問題 DP
整數劃分的定義 n m1 m2 mi 其中mi為正整數,並且1 mi n 則為n的乙個劃分。如果中的最大值不超過m,即max m1,m2,mi m,則稱它屬於n的乙個m劃分。這裡我們記n的m劃分的個數為f n,m 舉個例子,當n 5時我們可以獲得以下這幾種劃分 注意,例子中m 5 5 5 4 1 3...
整數劃分問題遞迴
整數劃分問題是演算法中的乙個經典命題之一,有關這個問題的講述在講解到遞迴時基本都將涉及。所謂整數劃分,是指把乙個正整數n寫成如下形式 n m1 m2 mi 其中mi為正整數,並且1 mi n 則為n的乙個劃分。如果中的最大值不超過m,即max m1,m2,mi m,則稱它屬於n的乙個m劃分。這裡我們...
整數劃分問題 遞迴
1.程式直接或間接呼叫自身的程式設計技巧稱為遞迴演算法 recursion 2.乙個過程或函式在其定義或說明中又直接或間接呼叫自身的一種方法,它通常把乙個大型複雜的問題層層轉化為乙個與原問題相似的規模較小的問題來求解,遞迴策略只需少量的程式就可描述出解題過程所需要的多次重複計算,大大地減少了程式的 ...