有 n 种物品,物品 j 的重量和价值分别为 wj 和 vj ,如果背包的最大容量限制是 m ,怎么样选择放入背包的物品以使得背包的总价值最大?
组合优化问题,设 xj 表示装入背包的第 j 个物品的数量,解可以表示为 <x1,x2,…,xn> 。那么目标函数和约束条件是:
如果组合优化问题的目标函数和约束条件都是线性函数,称为线性规划。如果线性规划问题的变量 xj 都是非负整数,则称为整数规划问题。背包问题就是整数规划问题。限制所有的 xj=0orxj=1 时称为0-1背包。
有n件物品和容量为m的背包,给出每件物品的重量 wi 以及价值 vi ,求解让装入背包的物品重量不超过背包容量,且价值最大 。特点:每个物品只有一件供你选择放还是不放。
二维解法:设 f[i][j] 表示前 i 件物品 总重量不超过 j 的最大价值,可得出状态转移方程:
f[i][j]=max{f[i−1][j−w[i]]+v[i], f[i−1][j]}
一维解法:设 f[j] 表示重量不超过 j 公斤的最大价值,可得出状态转移方程:
f[j]=max{f[j], f[j−w[i]]+v[i]}
有 n 件物品和容量为 m 的背包,给出每件物品的重量 wi 以及价值 vi ,求解让装入背包的物品重量不超过背包容量,且价值最大 。特点:每个物品可以无限选用。
一维解法: 设 f[j] 表示重量不超过 j 公斤的最大价值,可得出状态转移方程:
f[j]=max{f[j], f[j−w[i]]+v[i]}
有 n 件物品和容量为 m 的背包。给出每件物品的数量 si 、重量 wi 以及价值 vi ,求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。特点: 每个物品都有了一定的数量。
二维解法:把 si 个物品逐个拆分,则得到 ∑si 个物品,原问题转化为01背包问题,时间复杂度为 O(m×∑si) 。也可以在转移得过程中枚举 k ,代表第 i 个物品选取的数量,则转移方程为:
f[i][j]=0≤k≤s[i]max{f[i−1][j−k×v[i]]+k×w[i]}
一维解法:设 f[j] 表示重量不超过 j 公斤的最大价值,可得出状态转移方程:
f[j]=0≤k≤s[i]max{f[j−k×w[i]]+k×v[i]}
优化:
1、二进制优化
将第 i 种物品分成若干件物品,可以有 (vi,wi),(vi×2,wi×2),(vi×4,wi×4)… 等等,每件物品有一个系数,分别为 1,2,4,…,2k−1,n−2k+1 , k 是满足的最大整数。例如, n 为13,拆分成 1,2,4,6 四件物品,根据二进制的性质, 0…13 都可以由这四件物品组合得到。于是, n 件物品,就拆分成至多 logn 件物品。再转化为01背包问题求解,复杂度降至 O(m×∑logn) 。
2、单调队列优化
将背包的容量按照的剩余分成组:
仔细观察转移方程,会发现真正的转移只会发生在同一组内,不同的组是不可能转移的可以改一下上面的转移方程:
f[b+x×v[i]]=0≤k≤s[i]max{f[b+(x−k)×v[i]]+k×w[i]}
令 t=x−k ,状态转移方程变成:
f[b+x×v[i]]=x−s[i]≤t≤xmax{f[b+t×v[i]]−t×w[i]}+x×w[i]
这里的转移实际上一对 (b,x) 唯一对应一个 j 。我们固定 b ,那么对于 x 的决策 t 的集合正好是一个滑动区间,而 max 里面是一个和 x 无关的式子,所以可以用单调队列来维护。具体算法如下:
枚举一个 i 和 b(0≤b≤v[i]) ;
从小到大枚举 x ,并且用单调队列来维护 f[b+t×v[i]]−t×w[i] 关于 t 单调下降,更新 f[b+x×v[i]] 的值。
由于每个剩余类最多只有 v[i]m 个元素,那么对于一个 i ,转移的总时间为 O(v[i]m×v[i])=O(m) ,所以算法的时间复杂度为 O(n×m) ,这样我们就又优化掉了多重背包中的物品个数这一维。