背包问题求方案数
题目描述
核心思路
思路如下图所示:
示例:
问题:如何理解
c[0]=1
呢?
c [ 0 ] c[0] c[0]表示把背包容量为0时最优选法的方案数,我们可以理解为把某个物品放入背包容量为0时获得的价值是0是合法方案,因此当i取0时,也算一种方案。
问题:如何求解恰好装满01背包,并且最优选法的方案数呢?
什么是恰好装满,并且最优选法呢?比如上图的例子,有3种方案:第一种方案是5 8表示选择了第1件物品,体积是5,总价值是8;第二种方案是选择第2件物品和第3件物品,总体积是2+3=5,总价值是4+4=8;第三种方案是选择第5件物品,体积是4,总价值是8。由于题目给定的背包容量是5,因此上面只有第一种方案和第二种方案是恰好装满的,而第三种方案只是用了4,还剩余1个体积没有用掉,因此这并不是恰好装满。
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010,mod=1e9+7;
int n,m;
int f[N]; //f[i]表示背包容量为i时能装入物品的最大价值
int c[N]; //c[i]表示背包容量为i时最优选法的方案数
int main()
{
scanf("%d%d",&n,&m);
//体积从0到m,初始化方案数为1 特别注意c[0]也要是1
//因为体积为0它也是一种方案,可以立即为"把某个物品放入容量为0的背包时的最大价值是0"
//而这也是一种合法的方案
for(int i=0;i<=m;i++)
c[i]=1;
//跑一边01背包
for(int i=1;i<=n;i++)
{
int v,w;
scanf("%d%d",&v,&w);
for(int j=m;j>=v;j--)
{
//容量从j-v到j,只是多装入了一件物品,并没有改变方案数
if(f[j-v]+w>f[j])
{
f[j]=f[j-v]+w;
c[j]=c[j-v];
}
//不装入新物品,容量j已有的方案数为c[j]
//装入新物品,容量j对应的方案数为c[j-v]
//就比如题目给定5 5 然后有{体积,价值}为【{5,8}】、【{2,4}和{3,4}】、【{4,8}】
//可以发现最大价值都为8,但是是三种不同的方案
else if(f[j-v]+w==f[j])
{
c[j]=(c[j]+c[j-v])%mod;
}
}
}
printf("%d\n",c[m]);
return 0;
}