背包问题:给定一组物品,每个物品的费用和价值是不同的,求怎样选择物品使得在不超过限定总费用的条件下最后的总价值最大。
01背包是背包系列问题的基础,其解决问题的思想很重要。01背包问题限定每个物品的个数是1,开始已知物品的总数N和限定费用V,接着我们会得到物品的价值val[i]和费用vei[i]。一开始接触01背包,自己总联想到贪心,想按照性价比由高到低排个序,然后直接把前面几个物品的价值加起来就可以了(实际上物品可以放一部分的背包问题能用贪心策略做,但是01背包属于要么放要么不放的问题所以适合用DP做,但这样的思路是不严密的,比如:
N=5 V=18
wei: 14 12 2 3 4
val: 28 24 3 4 5
val/wei: 2 2 1.5 1.3 1.25
这样得到的结果是28,显然31才是最大值。
N=3 V=18
wei:16 15 3
val:32 29 5
结果是32,真实值是34.
用贪心的思路做必然会在各个步骤都要判断费用V是否还够用,即使某一步可以把当前的物品放进"袋子",那么也有可能阻断了后面的更优解的产生。解决01背包问题有这样重要的中间分析过程:对于第i个物品放进和不放进取决于放进后比原来的值大或者小,虚拟有很多个背包,这些能放第i个物品的背包的容量是V--wei[i],每一个背包都要试放,对于每个物品都进行了这样的操作,最后就能得到结果Val[V].状态转移方程:f[i][w]=max(f[i-1][w-wei]+val[i],f[i-1][w]) 将其优化成一维数组形式:
for(i=1-->N)
for(j=V-->wei[i])
Val[j]=max(Val[j-wei[i]]+val[i],Val[j]);
如果要求恰好装满背包(空间用完)的最优解:则初始化val[0]=0,其他所有的Val是-0x3f3f3f3f(负无穷)【因为开始时无解,无法恰好装满】。
如果要求空间够用的情况下的最优解:则初始化所有的Val是0【一个物品都不放,价值当然是0】。
例子:
杭电的简单01背包问题:http://acm.hdu.edu.cn/showproblem.php?pid=2602
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int wei[1005],val[1005],Val[1005];
int zerone(int val[],int wei[],int N,int V){
int i,j;
memset(Val,0,sizeof(Val));
for(i=0;i<N;i++){
for(j=V;j>=wei[i];j--){ //在所有能装下的背包中都试装
int tmp=Val[j-wei[i]]+val[i];
Val[j]=tmp>Val[j]?tmp:Val[j];
}
//for(j=1;j<=V;j++)cout<<Val[j]<<" "; cout<<endl;
}
return Val[V];
}
int main(int argc, char *argv[]) {
//freopen("cin.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--){
int N,V,i;
scanf("%d%d",&N,&V);
for(i=0;i<N;i++)scanf("%d",&val[i]);
for(i=0;i<N;i++)scanf("%d",&wei[i]);
int ans=zerone(val,wei,N,V);
printf("%d\n",ans);
}
return 0;
}