03:複雜的整數劃分問題
總時間限制: 200ms 記憶體限制: 65536kB 描述
將正整數n 表示成一系列正整數之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 ,k>=1 。
正整數n 的這種表示稱為正整數n 的劃分。
輸入 標準的輸入包含若干組測試資料。每組測試資料是一行輸入資料,包括兩個整數N 和 K。
(0 < N <= 50, 0 < K <= N) 輸出 對於每組測試資料,輸出以下三行資料:
第一行: N劃分成K個正整數之和的劃分數目
第二行: N劃分成若干個不同正整數之和的劃分數目
第三行: N劃分成若干個奇正整數之和的劃分數目 範例輸入
5 2
範例輸出
233
這道題有三個問題,一是有個數限制的整數劃分,二是不同數的劃分,三是劃分成奇數
在考慮有限個數整數劃分時,我們可以設狀態dp[i][j],表示將整數i劃分成j個整數
那麼就有dp[i][j]=dp[i-j][j]+dp[i-1][j-1]。
dp[i-j][j]有j個整數(都不為1),每個整數都同時減1。
dp[i-1][j-1]表示當前構成i的整數中有1,去掉裡面的1。
至於劃分為不重複整數和奇數,既可以枚舉整數或奇數做0-1背包,也可以通過第一個問題的方法轉移 dp[i][j]=dp[i-j][j-1]+dp[i][j-1]; | j<=i;(上限合適)->劃分成的數裡有j+劃分成的數裡沒有j
dp[i][j]=dp[i][i]; | j>i (上限過大) 只是奇數做DP時要注意枚舉情況
#include<cstdio>#include<iostream>#include<cstring>using namespace std;#define MAXN 50#define MAXM#define INF 0x3f3f3f3ftypedef long long int LL;int N,K;LL dp[MAXN+10][MAXN+10];void work1()//選K個{memset(dp,0,sizeof(dp));for(int i=1;i<=N;++i)//將i分成1個數只有一種方案dp[i][1]=1;for(int i=1;i<=N;++i) for(int j=2;j<=i;++j)//將每個數統一減1,或去掉當前數中的1dp[i][j]=dp[i-j][j]+dp[i-1][j-1];printf("%d\n",dp[N][K]);//把N分成K個數}void work2()//任意不同{memset(dp,0,sizeof(dp)); dp[0][0]=1; for(int i=0;i<=N;i++){ for(int j=1;j<=N;j++){//當前有數是j和降低上限if(j<=i)dp[i][j]=dp[i-j][j-1]+dp[i][j-1];else dp[i][j]=dp[i][i];//上限應為i}}printf("%lld\n",dp[N][N]);//劃分N,上限為N}void work3()//任意奇數(基本同work1){memset(dp,0,sizeof(dp));for(int i=0;i<=N;++i){dp[i][1]=1;if(i&1)dp[0][i]=1;//預先處理第0層}for(int i=1;i<=N;i++){for(int j=1;j<=N;j++){if(j&1)//同work1{if(j<=i)dp[i][j]=dp[i-j][j]+dp[i][j-1];else dp[i][j]=dp[i][i];}else dp[i][j]=dp[i][j-1];//當前非奇數}}printf("%lld\n",dp[N][N]);}int main(){while(~scanf("%d%d",&N,&K)){work1();work2();work3();}}