动态规划算法-07背包问题进阶
Posted 周先森爱吃素
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划算法-07背包问题进阶相关的知识,希望对你有一定的参考价值。
简介
我们在本专栏之前的文章介绍了基础的01背包问题及其解题思路,本文我们将讲述其拓展题型,也就是完全背包问题和多重背包问题。
01背包问题
首先,我们先来简单回顾一下经典的01背包问题,关于01背包的详细讲解可以参考我的博客。
给定 N N N件物品和一个容量为 W W W的背包,第 i i i件物品的价值为 v i v_i vi,重量为 w i w_i wi,求解哪些物品装入背包使得这些物品的重量不超过背包容量且价值总和最大。
这是最基础的背包问题,它的特点是每种物品仅有一件,你只能选或者不选,因此叫做01背包问题。我们定义问题的状态 f [ i , j ] f[i, j] f[i,j]表示前 i i i件物品放入容量为 j j j的背包可以获得的最大价值,显然其状态转移方程如下。
f
[
i
,
j
]
=
m
a
x
f
[
i
−
1
,
j
]
,
f
[
i
−
1
,
j
−
w
i
]
+
v
i
f[i, j] = max\\ f[i-1, j], f[i-1, j-w_i] + v_i \\
f[i,j]=maxf[i−1,j],f[i−1,j−wi]+vi
这个方程非常重要,几乎所有的背包问题都可以在这个方程上找到影子。因此我们不妨来分析一下:
将前 i i i件物品放入容量为 j j j的背包中,这个子问题,若只考虑第 i i i件物品的策略(即选或不选),那么就可以转化为一个只牵扯前 i − 1 i-1 i−1件物品的问题。若不选择第 i i i件物品,那么问题转化为“前 i − 1 i-1 i−1件物品放入容量为 j j j的背白中”;若选择第 i i i件物品,那么问题转化为“前 i − 1 i-1 i−1件物品放入剩余容量为 j − w i j-w_i j−wi的背包中”,此时获得的最大价值为 f [ i − 1 ] [ j − w i ] f[i-1][j-w_i] f[i−1][j−wi]加上放入第 i i i件物品的价值 v i v_i vi。当然,这两种情况还需要考虑第 i i i件物品能否放得进背包才可。
完全背包问题
问题描述
完全背包问题描述如下。
有 N N N种物品以及一个容量为 W W W的背包,每种物品都有无限件,第 i i i种物品的重量为 w i w_i wi且价值为 v i v_i vi。求解将哪些物品放入背包可以使得这些物品的重量不超过背包容量且价值总和最大。
基本思路
这个问题很类似于01背包问题,不同的是每种物品不是一件而是无数件,也就是说,从每种物品的角度看,对其的策略并非取还是不取两种,而是取0件、取1件…直至取 ⌊ W / w i ⌋ \\lfloor W / w_i \\rfloor ⌊W/wi⌋件。如果按照01背包的思路,记 f [ i , j ] f[i, j] f[i,j]为前 i i i种物品恰放入容量为 j j j的背包中的最大价值,仍旧可以按照每种物品不同的策略写出状态转移方程,如下。
f [ i , j ] = m a x f [ i − 1 , j − k w i ] + k v i ∣ 0 ⩽ k w i ⩽ j f[i, j] = max\\ f[i-1, j-kw_i] + k v_i | 0 \\leqslant kw_i \\leqslant j \\ f[i,j]=maxf[i−1,j−kwi]+kvi∣0⩽kwi⩽j
这和01背包一样有 O ( W N ) O(WN) O(WN)个状态需要求解,但是求解每个状态的时间不再是常数级别的了,求解状态 f [ i , j ] f[i, j] f[i,j]的时间为 O ( j w i ) O(\\fracjw_i) O(wij),总的复杂度可以认为是 O ( N W ∑ W w i ) O(NW\\sum\\fracWw_i) O(NW∑wiW),显然这个复杂度是比较大的。
这个思路源于01背包问题的基本思路,这说明01背包问题确实是很重要的,可以推广到其他类型的背包问题上,但是这个复杂度是需要改进的。
简单优化
完全背包有一个简单有效的优化,具体而言,若两件物品 i i i、 j j j满足 w i ⩽ w j w_i \\leqslant w_j wi⩽wj且 v i ⩾ v j v_i \\geqslant v_j vi⩾vj,那么物品 j j j可以直接去掉,无需考虑。 这个策略的正确性是显而易见的,任何情况下都可以将价值小重量大的 j j j换为物美价廉的 i i i,得到的方案至少不会更差。对于随机生成的数据,这个方法会大大减少物品的件数,加快速度。但是,这种策略并不能改善最坏情况的复杂度,因为精心设计的数据可能一件物品也去不掉。
这个优化的思路可以简单的 O ( N 2 ) O(N^2) O(N2)实现。而且背包问题有种不错的方法:首先去除费用大于背包容量 W W W的物品,然后使用类似计数排序的做法,计算出重量相同的物品中价值最高的是哪个,在 O ( W + N ) O(W+N) O(W+N)的时间内可以完成这个优化。
问题转化求解
01背包是最基本的背包问题,可以考虑将完全背包问题转化为01背包问题来求解。
最直观的做法是,考虑到第 i i i种物品最多选 ⌊ W / w i ⌋ \\lfloor W / w_i \\rfloor ⌊W/wi⌋,于是可以将第 i i i种物品转化为 ⌊ W / w i ⌋ \\lfloor W / w_i \\rfloor ⌊W/wi⌋件费用及价值均不变的物品,然后求解这个01背包问题。虽然这个简单粗暴的做法并没有改进时间复杂度,但是指明了完全背包转化为01背包问题的思路:将一种物品拆分为多个只能选0件或者1件的01背包中的物品。
更加高效的转化方法为:把第 i i i种物品拆分为重量为 w i 2 k w_i2^k wi2k、价值为 v i 2 k v_i2^k vi2k的若干件物品,其中 k k k取遍满足 w i 2 k ⩽ W w_i2^k \\leqslant W wi2k⩽W的非负整数。这是二进制的思想,背后的原因是,不管最优策略选择了几件第 i i i种物品,其件数写成二进制之后,总可以表示为若干个 2 k 2^k 2k件物品的和。这样一来就把每种物品拆分为 O ( l o g ⌊ W / w i ⌋ ) O(log\\lfloor W / w_i \\rfloor) O(log⌊W/wi⌋)件物品,是一个较大的改进。
O ( W N ) O(WN) O(WN)的算法
直接看这个算法的伪代码。
f[0...W] <--- 0
for i <--- 1 to N
for j <--- w_i to W
f[j] <--- max(f[j], f[j-w_i] + v_i)
我们发现一个有趣的地方,那就是这个伪代码与01背包问题的伪代码只有 j j j的循环次序不一样。为什么这个算法是可行的呢?这里我们不妨回忆一下为什么01背包中对于 j j 求动态规划0-1背包算法解释