题目描述
有 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; }