Buy the souvenirs---hdu2126(01背包输出方案数)

题目链接: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.
");
        else printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.
", f[m], dp[m]);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zhengguiping--9876/p/5025872.html