有 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;
}