题目描述
有 n 种物品,第 i 种物品有 a_i 个,不同种类的物品可以互相区分, 但相同种类的无法区分。从这些物品中取出 m 个, 有多少种取法? 求出数模 M 的余数.:
解析
考虑使用动态规划方法进行实现,定义状态方程为:dp[i][j]=前 i 类物品中取出 j 个的组合总数。可以得到以下状态转移方程:

以上递推公式包含三层循环,时间复杂度为 O(n*m^2),显然是过高了,于是我们考虑进行优化:
首先分为两种情况进行讨论:
1)j<=a[i],即(j-1<a[i])时:原式可以展开为 dp[i+1][j]=dp[i][j]+dp[i][j-1]+···+dp[i][0],可以注意到 dp[i+1][j-1]=dp[i][j-1]+···+dp[i][0],因此有 dp[i+1][j]=dp[i][j]+dp[i+1][j-1]。
2)j>a[i]时:原式可以展开为 dp[i+1][j]=dp[i][j]+dp[i][j-1]+···+dp[i][j-a[i]],注意到 dp[i+1][j-1]=dp[i][j-1]+···+dp[i][j-a[i]-1],因此 dp[i+1][j]=dp[i][j]+dp[i+1][j-1]-dp[i][j-a[i]-1]。
代码实现(C++)
#include<bits/stdc++.h>
using namespace std;
int n,m,M;
int a[1001];
int dp[1001][1001];
int main(){
cin>>n>>m>>M;
for(int i = 0;i < n;i++){
cin>>a[i];
}
for(int i = 0; i <= n; i++){
dp[i][0] = 1;
}
for(int i = 0; i < n; i++){
for(int j = 1; j <= m; j++){
if(j - 1 - a[i] >= 0){
dp[i + 1][j] = (dp[i][j] + dp[i + 1][j - 1] - dp[i][j - 1 - a[i]] + M) % M;
}else{
dp[i + 1][j] = (dp[i][j] + dp[i + 1][j - 1]) % M;
}
}
}
cout<<"解为:"<<dp[n][m]<<endl;
return 0;
}



