编程训练——有关计数的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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章