1、 01 背包
(1)题目:
有N件物品和一个容量为V 的背包。放入第i件物品耗费的空间是Ci,得到 的价值是Wi。求解将哪些物品装入背包可使价值总和最大。
(2)分析:
这是最简单的背包问题,一个物品只有放与不放两种情况
(3)状态转移方程:
定义F[ i , j ] 为前i个物品正好放入容量为j的背包中所获得的最大价值, 转移方程 F[ i, j ] = max{F[ i -1, j ] , F[ i-1 , j - w[i] ] + v[i] (j<V)}
(4)代码
f[0][0] = 0; for (int i=1;i<=n;i++) for (int j=w[i];j<=V;j--){ f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]); }
(5)空间优化
void ZeroOnePage(int C,int v);
for (int i=1;i<=n;i++) for (int j=V;j>=w;j--){ f[j]=max(f[j],f[j-w]+v) }
抽象成一个函数ZeroOnePage
void ZeroOnePage(int C,int W) { for (int j=V;j>=C;j--) F[j] = max(F[j],F[j-C]+W); }
为什么第二重循环要逆向? 如果仍未正向,F[ i, j ] = max{F[ i -1, j ] , F[ i-1 , j - w[i] ] + v[i] (j<V)},一维数组,f([i - 1])[j]已经被更改了
(6)初始化背包的细节问题
一般在这个地方有两种类型,一种题目要求恰好装满,另一种情况则没有要求,,,对于第一种情况的初始化,F[0 , 0] = 0 外,其余的都应该初始化为负无穷;对于第二种情况,全部初始为0便可
题目:见vijos
P1025 小飞侠的游园方案
|
(1)、题目
有N种物品和一个容量为V 的背包,每种物品都有无限件可用。放入第i种 物品的耗费的空间是Ci,得到的价值是Wi。求解:将哪些物品装入背包,可使 这些物品的耗费的空间总和不超过背包容量,且价值总和最大
(2)、分析
类似于0 1背包的,只是所不同的是物品可以取无限次,将0 1 背包取或不取两种状态变成不取或取0,1,2,3.。。。V/Ci件
仍按照0 1 背包的思路定义F[ i , j ] 为前i个物品正好放入容量为j的背包中所获得的最大价值, 转移方程 F[ i, j ] = max{F[ i -1, j ] , F[ i-1 , j - k*w[i] ] + k*v[i] (k*w[i]<V)}
时间复杂度为 O(VN*V/Ci),复杂度过于大,需想办法优化
(3)优化1
若两件物品i,j 满足 Ci<=Cj && Wi>=Wj 则将物品j直接去掉,不用考虑。
另外针对背包也可以这么优化,做一次计数排序,计算出费用相同价值最高的物品
(4)转化为 0 1 背包求解
因为每件物品最多选择 [V/Ci]件,可以构造一个[V/Ci]件价值和费用的0 1 背包,但这样的时间复杂度不变,但不乏为一种想法
高效想法:考虑二进制,把物品拆成Ci*2^k,Wi*2^k,其中k是取遍Ci*2^k<V的所有负整数,,,时间复杂度为O(log(V/C)*N),不失为一种好的优化方式
(5)O(VN)的解法(加空间优化)
void CompletePake(int w,int v);
for (int i=1;i<=n;i++) for (int j=w[i];j<=V;j--){ f[j]=max(f[j],f[j-w[i]]+v[i]); }
抽象成一个函数CompletePage
void CompletePage(int C,int W) { for (int j=C;j<=V;j++) F[j] = max(Fj],F[j-C]+W); }
可以思考一下完全背包为什么顺序循环?
3、多重背包
(1)、题目
有N种物品和一个容量为V 的背包。第i种物品最多有Mi件可用,每件耗费 的空间是Ci,价值是Wi。求解将哪些物品装入背包可使这些物品的耗费的空间 总和不超过背包容量,且价值总和最大。
(2)、分析:
与完全背包类似,只是把每个物品可选无限加了一个限定,转移方程F[i , j ] = max{F[ i-1, j ] , F[i-1,j-k*w[i]]+k*v[i] | k<=Mi }
(3)、优化
转化为 0 1 背包时间复杂度不变,暂不考虑,考虑二进制思想,把Mi件物品分成系数分别为2^1,2^2。。。2^k,Mi-2^k+1,(k为满足Mi-2^k+1>0的最大整数)
抽象成一个函数MultiplePage
void Multiple(int i,int C,int W,int M) { if (C*M <= V) { CompletePage(C,W); return; } k = 1; while (k < M) { ZeroOnePage(k*C,k*W); M = M-k; k <<= 1; } ZeroOnePage(M*C,M*W); }
(4)O(VN)的单调队列优化,未完待续ing
4、混合背包
(1)、问题
如果将前面1、2、3中的三种背包问题混合起来。也就是说,有的物品只可 以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取 的次数有一个上限(多重背包)。应该怎么求解呢
(2)、分析:
如果真正学会了前面三种背包,那混合背包完全就是前三种背包的一个联合体而已
for i = 1 to N if 第i件物品属于01背包 ZeroOnePack(Ci,Wi) else if 第i件物品属于完全背包 CompletePack(Ci,Wi) else if 第i件物品属于多重背包 MultiplePack(Ci,Wi,Ni)
小结:(转自背包九讲)有人说,困难的题目都是由简单的题目叠加而来的。这句话是否公理暂且存之不论,但他在本讲中得到了充分的体现。本来0 1 背包,完全背包,多重
背包都不是难题,但是将他们简单的组合起来以后就得到了一道一定能吓到不少人的题目。但只要基础扎实,领会三种背包问题的思想,就可以把复杂的问题拆分成简单的问题
来解决(借此与大家共勉)