背包问题求方案数

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出 最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

输出一个整数,表示 方案数 模 109+7 的结果。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 6

输出样例:

2

 思路:在用f[i]记录体积为i时的最大价值时,新开一个数组num[i]记录体积为i时的方案数,因为每个物品只装一次,所以这是一个01背包问题,遍历体积时仍需从大到小逆序遍历,但最后求的时体积恰好为m时的最大方案数,所以这里初始化时f[j]数组除了0号位置,其他位置均初始化为-inf,因为这样才能表示体积恰好为j时的最大价值,即:体积为0也就是什么也不装时(恰好体积为0,“恰好装满”),为合法状态,此时最大价值为0,其他体积状态均不是“恰好装满”,是非法状态,不能选,所以赋值为-inf,保证在取max时取不到该非法状态。(01背包初始化细节参考https://blog.csdn.net/qq_42804678/article/details/81662367对于num[]数组,初始化时,num[0]=1,表示体积为0的方案数为1,其他状态均有此递推过来,最后找到最大价值,遍历num[]数组,统计等于最大价值时的所有体积下的方案数之和sum即为答案。

完整代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1010;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int f[maxn],num[maxn];

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++) f[i]=-inf;
    num[0]=1;
    for(int i=0;i<n;i++){
        int v,w;
        cin>>v>>w;
        for(int j=m;j>=v;j--){
            int t=max(f[j],f[j-v]+w);
            int s=0;
            if(t==f[j]) s+=num[j];//s统计当前最大价值对应体积下的方案数之和
            if(t==f[j-v]+w) s+=num[j-v];
            if(s>mod) s-=mod;
            f[j]=t;
            num[j]=s;
        }
    }
    int ans=0;
    for(int i=0;i<=m;i++){
        ans=max(ans,f[i]);
    }
    int sum=0;
    for(int i=0;i<=m;i++){
        if(f[i]==ans){
            sum+=num[i];
            if(sum>mod)
                sum-=mod;
        }
    }
    cout<<sum<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值