格式
有n
个重量和价值分别为wi
,vi
的物品。从这些物品中挑选出总重量不超过W
的物品,求所有挑选方案中价值总和的最大值。在这里,每种物品可以挑选任意多件。
回顾
我们之前在解决0/1背包的时候,采用的是记忆化搜索的方式,把当前的情况存放在一个二维数组中,每当便利到这个新情况的时候,就拿当前新情况的值和之前的最优解进行比较,谁的价值更大就保留谁。
题解
我们可以在0/1背包的基础上进行修改,首先0/1背包的
i
表示物品,j
表示容量,我们只有两个选择,拿或者不拿
W[i][j]=max(W[i-1][j]/*这是不拿的情况*/,W[i][j-wi]+vi/*拿的情况*/)
现在我们相当于有了无限个物品i
,所以我们可以这么来,我们可以把两个物体看成一个新物体,体积翻倍,价值翻倍,三个,四个同理,只要在会看的过程中不把背包的容量变成负数,我们就可以拿n个物体再和之前的比较,写成代码的形式就是
int k=1;//k表示件数,从1件开始,由于我们不需要知道物品i拿了多少件,并不需要保存k
while(j>=k*w[i]){
W[i][j]=max(W[i-1][j],W[i][j-k*w[i]]);
}
这就是我们的转移方程了。
最后输出的方式和0/1背包一样。
插一嘴,这里并不一定要一个二维数组来存贮
我们可以用一维数组来记录结果,这个方法叫做滚动数组
因为每次拿东西的顺序是都是下一个,所以我们二维数组第一维(表示第几个物品)可以省略掉。
我们每次都从背包的最大容积处下手,然后倒着向左走。
为什么要倒着呢,因为正着向右走的话会把上一个物品的数据给覆盖掉,也就是说我们就丢失了不拿这个物品的记录了这就完蛋了~
使用的有点很显然,把空间复杂度为O(nv)
的优化O(v)
,妙哉啊~
AC代码(滚动数组版)
#include<iostream>
using namespace std;
const int MAX_SIZE=110;
const int MAX_RONGJI=10010;
int n,W,w[MAX_SIZE],v[MAX_SIZE],W_Bag[MAX_RONGJI];//物品数量,背包容积,物品体积,物品价值,后期存储数据用的一维数组
int main(){
cin >> n >> W;
for(int i=0;i<n;i++){//读入物品的容积和价值
cin >> w[i] >> v[i];
}
for(int i=0;i<n;i++){//标记物品
for(int j=W;j>0;j--){//标记背包容量
int k=1;
while(j>=(k*w[i])){//背包还装的下就行
W_Bag[j]=max(W_Bag[j],W_Bag[j-k*w[i]]+k*v[i]);
k++;
}
}
}
cout << W_Bag[W];
return 0;
}