题述
有n个无区别的物品,将它们划分成不超过m组,求出划分方法数模M的余数。
限制条件
1<=m<=n<=1000
2<=M<=10000
样例
输入
4 3
10000
输出
4
题记
dp[i][j]:j的i划分的总数
dp[i][j]=dp[i][j-i]+dp[i-1][j]
本题是来自<挑战程序设计竞赛>虽然书中说了该题的递推式的原因,但是不够清楚,我经过思考认为是这样的:我们在计算过程中只是计算总数,并不清楚到底该几个分成一组这样的细节。如果考虑n的m划分,每一组的物品数量为ai(1<=i<=m),如果说每个ai都满足ai>=1,那么每个ai-1之后就对应了n-m的m划分,这一点很好理解。另外一种情况,如果存在ai=0,意思就是说我们虽然分了m组,但是有些组的物品数是0,那么可以表示成dp[i-1][j]。这一点是难点。假设当前这样分有k个组都是0个物品(k>=0),那么我们可以利用前面算的dp[i-1][j],划分当中存在某些组为0肯定不行啊,我们只看第k个,它占用了一个划分,这是没有意义的,当然就可以写成dp[i-1][j],但是我们怎么知道到底有几个ai为0呢,难道还要对k循环吗?其实不用,因为dp[i-1][j]已经帮我们算了k>=1的情况,我们只用考虑第k个为0的情况。
附加注释
另外一点需要注意的就是dp[0][0]=1,这点很重要,这是之后计算的根本!!!
代码如下
#include <iostream>
//划分数
using namespace std;
const int Maxn=1005;
//输入
int m,n;
int M;
//j的i划分的总数
int dp[Maxn][Maxn];
void solve(){
//0个划分为0组有1种不同的划分方法
dp[0][0]=1;
for(int i=1;i<=m;i++){
for(int j=0;j<=n;j++){
if(j-i>=0) //物品个数比划分组数多
dp[i][j]=(dp[i-1][j]+dp[i][j-i])%M;
else //划分组数比物品个数还要多(存在一些组存在1个物品都没有)
dp[i][j]=dp[i-1][j];
}
}
printf("%d\n",dp[m][n]);
}
int main()
{
scanf("%d %d",&n,&m);
scanf("%d",&M);
solve();
return 0;
}