NYOJ90 整数划分(经典递归和dp)
整數(shù)劃分
時(shí)間限制:3000?ms ?|? 內(nèi)存限制:65535?KB 難度:3 描述其中n1≥n2≥…≥nk≥1,k≥1。?
正整數(shù)n的這種表示稱為正整數(shù)n的劃分。求正整數(shù)n的不?
同劃分個(gè)數(shù)。?
例如正整數(shù)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。?
輸入
思路:
遞歸法:
整數(shù)劃分問題是算法中的一個(gè)經(jīng)典命題之一,有關(guān)這個(gè)問題的講述在講解到遞歸時(shí)基本都將涉及。所謂整數(shù)劃分,是指把一個(gè)正整數(shù)n寫成如下形式:
? ? ? ?n=m1+m2+...+mi; (其中mi為正整數(shù),并且1 <= mi <= n),則{m1,m2,...,mi}為n的一個(gè)劃分。
? ? ? ?如果{m1,m2,...,mi}中的最大值不超過m,即max(m1,m2,...,mi)<=m,則稱它屬于n的一個(gè)m劃分。這里我們記n的m劃分的個(gè)數(shù)為f(n,m);
? ? ? ?例如但n=4時(shí),他有5個(gè)劃分,{4},{3,1},{2,2},{2,1,1},{1,1,1,1};
? ? ? ?注意4=1+3 和 4=3+1被認(rèn)為是同一個(gè)劃分。
? ? ? ?該問題是求出n的所有劃分個(gè)數(shù),即f(n, n)。下面我們考慮求f(n,m)的方法;
? ? ? ?根據(jù)n和m的關(guān)系,考慮以下幾種情況:?
? ? ? ?(1)當(dāng) n = 1 時(shí),不論m的值為多少(m > 0 ),只有一種劃分即 { 1 };
? ? ? ? (2) ?當(dāng) m = 1 時(shí),不論n的值為多少,只有一種劃分即 n 個(gè) 1,{ 1, 1, 1, ..., 1 };
? ? ? ? (3) ?當(dāng) n = m 時(shí),根據(jù)劃分中是否包含 n,可以分為兩種情況:
? ? ? ? ? ? ? (a). 劃分中包含n的情況,只有一個(gè)即 { n };
? ? ? ? ? ? ? (b). 劃分中不包含n的情況,這時(shí)劃分中最大的數(shù)字也一定比 n 小,即 n 的所有 ( n - 1 ) 劃分。
? ? ? ? ? ? ? 因此 f(n, n) = 1 + f(n, n-1);
? ? ? ? (4) 當(dāng) n < m 時(shí),由于劃分中不可能出現(xiàn)負(fù)數(shù),因此就相當(dāng)于 f(n, n);
? ? ? ? (5) 但 n > m 時(shí),根據(jù)劃分中是否包含最大值 m,可以分為兩種情況:
? ? ? ? ? ? ? ?(a). 劃分中包含 m 的情況,即 { m, { x1, x2, ..., xi } }, 其中 { x1, x2, ..., xi } 的和為 n - m,可能再次出現(xiàn) m,因此是(n - m)的 m 劃分,因此這種劃分
? ? ? ? ? ? ? ? ? ? ?個(gè)數(shù)為 f(n-m, m);
? ? ? ? ? ? ? ?(b). 劃分中不包含 m 的情況,則劃分中所有值都比 m 小,即 n 的 ( m - 1 ) 劃分,個(gè)數(shù)為 f(n, m - 1);
? ? ? ? ? ? ? 因此 f(n, m) = f(n - m, m) + f(n, m - 1);
? ? ? ? ?綜合以上情況,我們可以看出,上面的結(jié)論具有遞歸定義特征,其中(1)和(2)屬于回歸條件,(3)和(4)屬于特殊情況,將會(huì)轉(zhuǎn)換為情況(5)。而情況(5)為通用情況,屬于遞推的方法,其本質(zhì)主要是通過減小m以達(dá)到回歸條件,從而解決問題。其遞推表達(dá)式如下:
? ? ? ? ?f(n, m) = ? ? ?1; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?( n = 1 or m = 1 )
? ? ? ? ? ? ? ? ? ? ? ? ? ? f(n, n); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ( n < m )
? ? ? ? ? ? ? ? ? ? ? ? ? ? 1+ f(n, m - 1); ? ? ? ? ? ? ? ? ? ? ?( n = m )
? ? ? ? ? ? ? ? ? ? ? ? ? ? f(n - m, m) + f(n, m - 1); ? ? ? ( n > m )
以上講解來自于:http://www.cnblogs.com/hoodlum1980/archive/2008/10/11/1308493.html
看了這個(gè),代碼就很簡單了
動(dòng)態(tài)規(guī)劃法:
數(shù)組dp[N][M]表示N為被劃分?jǐn)?shù),M為劃分?jǐn)?shù)的最大值,此題M==N,故即求dp[N][N];
1>狀態(tài)轉(zhuǎn)移方程:
dp[N][M]=dp[N][M-1]+dp[N-M][M];
該怎樣理解呢?這里分兩步:
Step 1:所劃分的最大數(shù)不包括M,即每個(gè)劃分?jǐn)?shù)都是小于M的,此時(shí)總數(shù)為dp[N][M-1].
Step 2:所劃分的最大數(shù)包括M,那么這一步被劃分?jǐn)?shù)就應(yīng)該減去一個(gè)M,此時(shí)總數(shù)為dp[N-M][M].
到這里就是完整的思路了,應(yīng)該注意的是上面的劃分,劃分?jǐn)?shù)里有重復(fù)的數(shù),那么如果要求劃分?jǐn)?shù)沒有重復(fù)的呢,該怎樣求呢?
這里的狀態(tài)轉(zhuǎn)移方程和上面就有點(diǎn)細(xì)微區(qū)別了.先來看看方程:
2>dp[N][M]=dp[N][M-1]+dp[N-M][M-1];
其實(shí)聯(lián)系1中的步驟就不難理解了,同樣分為兩步:
Step 1:所劃分的最大數(shù)不包括M,即每個(gè)劃分?jǐn)?shù)都是小于M的,此時(shí)總數(shù)也是dp[N][M-1].
Step 2:所劃分的最大數(shù)包括M,那么劃分就的相應(yīng)的減去M,注意到不能重復(fù),即M劃分?jǐn)?shù)出現(xiàn)的次數(shù)只能為1.所以M就得換成M-1了,即dp[N-M][M-1].
3>在拓展一下,要是劃分的個(gè)數(shù)為確定的數(shù)呢?即dp[N][K].表示N被劃分成K個(gè)數(shù).
這時(shí)狀態(tài)轉(zhuǎn)移方程就為
dp[N][K]=dp[N-K][K]+dp[N-1][K-1].
應(yīng)該這樣理解:
Step 1:被劃分的K個(gè)數(shù)中不包括1,那么就應(yīng)該先自動(dòng)的為其分配1,K個(gè)數(shù)共N-K,剩下的數(shù)自由分配,總能保證其值大于2,即dp[N-K][K].
Step 2:存在一個(gè)數(shù)為1的情況,此時(shí)剩下的N-1分給K-1個(gè)數(shù),即dp[N-1][K-1].
來源:http://www.cnblogs.com/xl1027515989/p/3603533.html
代碼1(遞歸):
#include <cstdio> #include <cstring> #include <string> #include <set> #include <iostream> #include <cmath> #include <stack> #include <queue> #include <vector> #include <algorithm> #define mem(a,b) memset(a,b,sizeof(a)) #define inf 0x3f3f3f3f #define mod 10000007 #define debug() puts("what the fuck!!!") #define ll long long using namespace std; int f(int n,int m)//f(n,m)定義的是n的m劃分,m是能組成n的數(shù)中的最大值 {if(n==1||m==1) return 1;if(n==m) return f(n,n-1)+1;if(n<m) return f(n,n);if(n>m) return f(n-m,m)+f(n,m-1); } int main() {int t,n;scanf("%d",&t);while(t--){scanf("%d",&n);printf("%d\n",f(n,n));}return 0; } View Code?
代碼2(DP):
#include <cstdio> #include <cstring> #include <string> #include <set> #include <iostream> #include <cmath> #include <stack> #include <queue> #include <vector> #include <algorithm> #define mem(a,b) memset(a,b,sizeof(a)) #define inf 0x3f3f3f3f #define mod 10000007 #define debug() puts("what the fuck!!!") #define ll long long using namespace std; int dp[15][15];//dp[n][m],n為被劃分?jǐn)?shù),m為劃分?jǐn)?shù)中的最大值 int main() {int t,n;scanf("%d",&t);while(t--){mem(dp,0);scanf("%d",&n);for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(i==j)dp[i][j]=dp[i][j-1]+1;else if(i<j)dp[i][j]=dp[i][i];else if(i>j)dp[i][j]=dp[i-j][j]+dp[i][j-1];}}printf("%d\n",dp[n][n]);}return 0; } View Code?
轉(zhuǎn)載于:https://www.cnblogs.com/Roni-i/p/8614919.html
總結(jié)
以上是生活随笔為你收集整理的NYOJ90 整数划分(经典递归和dp)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑画折线图(电脑怎么绘制折线图)
- 下一篇: linq内联左联