题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2126
有n个物品每个物品的价格是v[i],现在有m元钱问最多买多少种物品,并求出有多少种选择方法;
如果将物品的价格看做容量,将它的件数1看做价值的话,那么用01背包就可以求的花费m钱所能买到的最大件数dp[m]。
但是题目还要求方案数,因此很容易想到再建立一个数组f[j],存储j元钱能买dp[j]个物品的方案数。
在求解01背包的过程中,要分两种情况讨论:
设当前所选的物品为i
1. 若选了物品i后,能买的件数比不选物品i的件数大,即dp[j-val[i]]>dp[j]
那么更新dp[j],同时,f[j]的方案数即为f[j-val[i]]
原因是:假设f[j-val[i]]的方案数为 AB AC 两种,那么在此情况下加个D,为ABD,ACD,仍为两种,所以f[j]=f[j-val[i]]即可
当然,要注意f[j-val[i]]为0的情况,因此当它为0时,f[j]=1,1即为D
2. 若选了物品i后,能买的件数比不选物品i的件数相同,即dp[j-val[i]]==dp[j]
即原先不选第i个物品,所需要的方案数为f[j];而选了物品i的方案数为f[j-val[i]]。
因此,总的方案数即为f[j]+f[j-val[i]]
当然,这里也要注意f[j-val[i]]=0的情况,当它为0时,f[j]+=1,1即为D
#include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> #include<iostream> using namespace std; #define N 550 #define met(a, b) memset(a, b, sizeof(a)) int dp[N], f[N], v[N], n, m; int main() { int T; scanf("%d", &T); while(T--) { met(dp, 0); met(f, 0); met(v, 0); scanf("%d %d", &n, &m); for(int i=1; i<=n; i++) scanf("%d", &v[i]); for(int i=1; i<=n; i++) { for(int j=m; j>=v[i]; j--) { if(dp[j]<dp[j-v[i]]+1)///物品i要选,例如已有ab和ac两种选法加个d还是两种选法; { dp[j] = dp[j-v[i]] + 1; if(!f[j-v[i]]) f[j] = 1; else f[j] = f[j-v[i]]; } else if(dp[j]==dp[j-v[i]]+1) { if(!f[j-v[i]]) f[j] += 1; else f[j] += f[j-v[i]]; } } } if(!dp[m])printf("Sorry, you can‘t buy anything.\n"); else printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.\n", f[m], dp[m]); } return 0; }
时间: 2024-11-05 16:09:26