背包问题总结
刷到背包了,背包是一类问题,开个总结记录贴
1 01 背包问题
题意:有N 件物品和一个容量为V 的背包。放入第i 件物品耗费的费用是Ci,得到的价值是Wi。求解将哪些物品装入背包可使价值总和最大
思路:用子问题定义状态:即f[i,v] 表示前i 件物品恰放入一个容量为v 的背包可以获得的最大价值。则其状态转移方程便是:
f[i,v]=max(f[i,v],f[i-1,v-ci]+wi)
伪代码
通过递减顺序V~0,我们可以在每个i循环完后得到最终的状态,即可以省略i
对于恰好装满背包,需要初始化为负无穷
对于只要求最大价值的,需要初始化为0
例题:poj 3624
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 using namespace std; 6 int dp[13500]; 7 int w[13500],v[13500]; 8 int main() 9 { 10 int n,W; 11 #ifndef ONLINE_JUDGE 12 freopen("1.in","r",stdin); 13 #endif 14 while(scanf("%d%d",&n,&W)!=EOF) 15 { 16 int i,j,k; 17 memset(dp,0,sizeof(dp)); 18 for(i=0;i<n;i++) 19 { 20 scanf("%d%d",&w[i],&v[i]); 21 } 22 for(i=0;i<n;i++) 23 { 24 for(j=W;j>=w[i];j--) dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 25 } 26 printf("%d\n",dp[W]); 27 } 28 return 0; 29 }
完全背包
有N 种物品和一个容量为V 的背包,每种物品都有无限件可用。放入第i 种物品的费用是Ci,价值是Wi。求解:将哪些物品装入背包,可使这些物品的耗费的费用总和不超过背包容量,且价值总和最大。
完全背包问题有一个很简单有效的优化,是这样的:若两件物品i、j 满足Ci<=Cj且Wi>=Wj,则将可以将物品j 直接去掉,不用考虑。
最简单的想法是,考虑到第i 种物品最多选⌊V /Ci⌋ 件,于是可以把第i 种物品转化为⌊V /Ci⌋ 件费用及价值均不变的物品,然后求解这个01 背包问题。这样的做法完全没有改进时间复杂度,但这种方法也指明了将完全背包问题转化为01 背包问题的思路:将一种物品拆成多件只能选0 件或1 件的01 背包中的物品。更高效的转化方法是:把第i 种物品拆成费用为Ci2k、价值为Wi2k 的若干件物品,其中k 取遍满足Ci2k V 的非负整数。这是二进制的思想。因为,不管最优策略选几件第i 种物品,其件数写成二进制后,总可以表示成若干个2k 件物品的和。这样一来就把每种物品拆成O(log ⌊V /Ci⌋) 件物品,是一个很大的改进。
为什么这个算法就可行呢?首先想想为什么01 背包中要按照v 递减的次序来循环。让v 递减是为了保证第i 次循环中的状态F[i; v] 是由状态F[i - 1; v - Ci] 递推而来。换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第i 件物品”这件策略时,依据的是一个绝无已经选入第i 件物品的子结果F[i - 1; v - Ci]。而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第i 种物品”这种策略时,却正需要一个可能已选入第i 种物品的子结果F[i; v - Ci],所以就可以并且必须采用v递增的顺序循环。这就是这个简单的程序为何成立的道理。
基础题目: hdu 1114
多重背包
题目:有N 种物品和一个容量为V 的背包。第i 种物品最多有Mi 件可用,每件耗费的空间是Ci,价值是Wi。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。
例题:hdu 1059