欢迎光临我的Blog,虽然这里还很简陋,但未来一定会焕发生机的!

多重集组合数问题(动态规划)——炎泽汐 の Blog

算法 yanzexi 3年前 (2022-10-07) 894次浏览 已收录 0个评论 扫描二维码
文章目录[隐藏]

题目描述

有 n 种物品,第 i 种物品有 a_i 个,不同种类的物品可以互相区分, 但相同种类的无法区分。从这些物品中取出 m 个, 有多少种取法? 求出数模 M 的余数.:

多重集组合数问题(动态规划)——炎泽汐 の Blog

多重集组合数问题(动态规划)——炎泽汐 の Blog

解析

考虑使用动态规划方法进行实现,定义状态方程为:dp[i][j]=前 i 类物品中取出 j 个的组合总数。可以得到以下状态转移方程:

多重集组合数问题(动态规划)——炎泽汐 の Blog
以上递推公式包含三层循环,时间复杂度为 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]。


综上,我们可以得到以下状态转移方程:
多重集组合数问题(动态规划)——炎泽汐 の Blog

代码实现(C++)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3.  
  4. int n,m,M;
  5. int a[1001];
  6. int dp[1001][1001];
  7.  
  8. int main(){
  9. cin>>n>>m>>M;
  10. for(int i = 0;i < n;i++){
  11. cin>>a[i];
  12. }
  13. for(int i = 0; i <= n; i++){
  14. dp[i][0] = 1;
  15. }
  16. for(int i = 0; i < n; i++){
  17. for(int j = 1; j <= m; j++){
  18. if(j - 1 - a[i] >= 0){
  19. dp[i + 1][j] = (dp[i][j] + dp[i + 1][j - 1] - dp[i][j - 1 - a[i]] + M) % M;
  20. }else{
  21. dp[i + 1][j] = (dp[i][j] + dp[i + 1][j - 1]) % M;
  22. }
  23. }
  24. }
  25. cout<<"解为:"<<dp[n][m]<<endl;
  26. return 0;
  27. }
喜欢 (10)
[炎泽汐de收款码]
分享 (0)

您必须 登录 才能发表评论!