Openjudge 百練 03:複雜的整數劃分問題
阿新 • • 發佈:2018-12-26
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
- 樣例輸出
-
2 3 3
這道題有三個問題,一是有個數限制的整數劃分,二是不同數的劃分,三是劃分成奇數
在考慮有限個數整數劃分時,我們可以設狀態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 0x3f3f3f3f typedef 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,或去掉當前數中的1 dp[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(); } }