题目描述:
将n个无区别的物品,划分成不超过m组,求出划分方法总数,输出总数模M的余数。
1
≤
n
,
m
≤
1000
1≤n,m≤1000
1≤n,m≤1000
2
≤
M
≤
10000
2≤M≤10000
2≤M≤10000
样例输入:
4 3 10000
将4划分为不超过3项: 1 + 1 + 2 、 2 + 2 、 1 + 3 、 4 1+1+2、2+2、1+3、4 1+1+2、2+2、1+3、4,有4种划分方法,所以输出 4 % 10000 = 4 4\%10000=4 4%10000=4
样例输出
4
算法
用动态规划, d p [ i ] [ j ] dp[i][j] dp[i][j]表示将 j j j划分成不超过 i i i组。
比如说dp[3][7]表示将7划分成不超过3组,划分方法如下:
1 + 1 + 5 1+1+5 1+1+5
1 + 2 + 4 1+2+4 1+2+4
1 + 3 + 3 1+3+3 1+3+3
2 + 2 + 3 2+2+3 2+2+3
2 + 5 + 0 2+5+0 2+5+0
1 + 6 + 0 1+6+0 1+6+0
3 + 4 + 0 3+4+0 3+4+0
7 + 0 + 0 7+0+0 7+0+0
一共有8种划分方法,现在观察上述8种划分,既然是划分成不超过3项,干脆给未满3项的划分补0,使之变成3项。这样一来,上述8种划分方式就被分为不含0的划分和含0的划分。
证 明 1 , 对 于 将 7 划 分 成 不 超 过 3 项 这 个 问 题 , 不 含 0 的 划 分 总 数 等 于 d p [ 3 ] [ 4 ] : 证明1,对于将7划分成不超过3项这个问题,不含0的划分总数等于dp[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项。
所以, d p [ 3 ] [ 7 ] dp[3][7] dp[3][7]中不含0的划分就等于 d p [ 3 ] [ 4 ] dp[3][4] dp[3][4]。
证 明 2 , 对 于 将 7 划 分 成 不 超 过 3 项 这 个 问 题 , 含 0 的 划 分 总 数 等 于 d p [ 2 ] [ 7 ] : 证明2,对于将7划分成不超过3项这个问题,含0的划分总数等于dp[2][7]: 证明2,对于将7划分成不超过3项这个问题,含0的划分总数等于dp[2][7]:
对于所有将7划分为不超过2项的划分结果,加个0便成为了将7划分为不超过3项的结果。
对于所有将7划分为不超过3项的划分结果中,含0的划分,去掉一个0便得到了将7划分为不超过2项的划分结果。
所以, d p [ 3 ] [ 7 ] dp[3][7] dp[3][7]中含0的划分就等于 d p [ 2 ] [ 7 ] dp[2][7] dp[2][7]。
综上所述,递推关系为:
d
p
[
i
]
[
j
]
=
{
d
p
[
i
]
[
j
−
i
]
+
d
p
[
i
−
1
]
[
j
]
,
j
≥
i
d
p
[
i
−
1
]
[
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[i][j]={dp[i][j−i]+dp[i−1][j],j≥idp[i−1][j],j<i
初始值为:
d
p
[
0
]
[
非
零
数
]
=
0
,
d
p
[
.
.
]
[
0
]
=
1
dp[0][非零数]=0,dp[..][0]=1
dp[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;
}