题目链接: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, ); met(f, ); met(v, ); scanf("%d %d", &n, &m);
for(int i=; i<=n; i++)
scanf("%d", &v[i]); for(int i=; i<=n; i++)
{
for(int j=m; j>=v[i]; j--)
{
if(dp[j]<dp[j-v[i]]+)///物品i要选,例如已有ab和ac两种选法加个d还是两种选法;
{
dp[j] = dp[j-v[i]] + ;
if(!f[j-v[i]]) f[j] = ;
else f[j] = f[j-v[i]];
}
else if(dp[j]==dp[j-v[i]]+)
{
if(!f[j-v[i]]) f[j] += ;
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 ;
}