編程訓練——有關計數的dp:劃分數

題目描述:

將n個無區別的物品,劃分成不超過m組,求出劃分方法總數,輸出總數模M的餘數。
1n,m10001≤n,m≤1000
2M100002≤M≤10000

樣例輸入:

4 3 10000

將4劃分爲不超過3項:1+1+22+21+341+1+2、2+2、1+3、4,有4種劃分方法,所以輸出4%10000=44\%10000=4

樣例輸出

4

算法

用動態規劃,dp[i][j]dp[i][j]表示將jj劃分成不超過ii組。

比如說dp[3][7]表示將7劃分成不超過3組,劃分方法如下:

1+1+51+1+5
1+2+41+2+4
1+3+31+3+3
2+2+32+2+3
2+5+02+5+0
1+6+01+6+0
3+4+03+4+0
7+0+07+0+0

一共有8種劃分方法,現在觀察上述8種劃分,既然是劃分成不超過3項,乾脆給未滿3項的劃分補0,使之變成3項。這樣一來,上述8種劃分方式就被分爲不含0的劃分和含0的劃分。

1730dp[3][4]證明1,對於將7劃分成不超過3項這個問題,不含0的劃分總數等於dp[3][4]:

對於所有將4劃分成不超過3項:1+1+2、2+2+0、1+3+0、4+0+0(補0保證每種劃分都是3項),將每種劃分中每一項都加1:2+2+3、2+2+1、2+4+1、5+1+1,發現這些都是將7劃分成不超過3項的結果中,不含0的劃分。
反之,7劃分成不超過3項的結果中,所有不含0的劃分,都能將每一項減1變成將4劃分爲不超過3項。
所以,dp[3][7]dp[3][7]中不含0的劃分就等於dp[3][4]dp[3][4]

2730dp[2][7]證明2,對於將7劃分成不超過3項這個問題,含0的劃分總數等於dp[2][7]:

對於所有將7劃分爲不超過2項的劃分結果,加個0便成爲了將7劃分爲不超過3項的結果。
對於所有將7劃分爲不超過3項的劃分結果中,含0的劃分,去掉一個0便得到了將7劃分爲不超過2項的劃分結果。
所以,dp[3][7]dp[3][7]中含0的劃分就等於dp[2][7]dp[2][7]

綜上所述,遞推關係爲:
dp[i][j]={dp[i][ji]+dp[i1][j],jidp[i1][j],j<i dp[i][j]=\left\{ \begin{aligned} dp[i][j-i]+dp[i-1][j],j≥i\\ dp[i-1][j],j<i \end{aligned} \right.

初始值爲:
dp[0][]=0,dp[..][0]=1dp[0][非零數]=0,dp[..][0]=1
沒有任何非零數是0個數的和,數字0無論分成幾項之和都只有一種方式。

代碼

#include<stdio.h>

#define maxn 1005

int n, m, M;
int dp[maxn][maxn];

int main(){
    while(scanf("%d %d", &n, &m)==2){
        for(int i = 0;i <= m;i++){
            dp[i][0] = 1;
        }
        for(int j = 1;j <= n;j++){
            dp[0][j] = 0;
        }
        scanf("%d", &M);
        for(int i = 1;i <= m;i++){
            for(int j = 1;j <= n;j++){
                if(j>=i){
                    dp[i][j] = dp[i-1][j] + dp[i][j-i];
                }
                else{
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        printf("%d\n", dp[m][n]%M);
    }

    return 0;
}
發佈了40 篇原創文章 · 獲贊 43 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章