背包问题
背包(资源型DP)
01背包
01背包(ZeroOnePack): 有N件物品和一个容量为V的背包。(每种物品均只有一件)第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
用dp[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。
状态转移方程
dp[i][v] = max(dp[i-1][v],dp[i-1][v-c[i]]+w[i])
状态转移方程的理解:
当把第i件物品放进容量v的背包时,前i-1件物品的状态已经确定,这时有两种情况
- 第i件物品不放进去,这时所得价值为:
dp[i-1][v] - 第i件物品放进去,这时所得价值为:
dp[i-1][v-c[i]]+w[i]
最后比较两种情况的价值大小,dp[i][v]是其中较大的。
模板
首先,我们是通过i从1到n的循环来依次表示前i件物品存入的状态。即:for i=1..N
现在思考如何能在dp[v]表示当前状态是容量为v的背包所得价值,而又使dp[v]和dp[v-c[i]]+w[i]表示前一状态的价值?
逆序!这就是关键。
for i=1...N
for v=V...0
f[v] = max{f[v],f[v-c[i]]+w[i]};
完全背包
完全背包(CompletePack): 有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
状态转移方程
dp[i][v] = max(dp[i-1][v-k*c[i]]+k*w[i]) (0<=k*c[i]<=V)
模板
dp[0...V]={0}
for i=1...N
for v=c[i]...V
dp[v] = max{dp[v], dp[v-c[i]]+w[i]}
这个伪代码与01背包问题的伪代码只有v的循环次序不同而已。为什么这个算法就可行呢?
首先想想为什么01背包中要按照v递减的次序来循环?
让v递减是为了保证第i次循环中的状态dp[i][v]是由状态dp[i-1][v-c[i]]递推而来。换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第i件物品”这件策略时,依据的是一个绝无已经选入第i件物品的子结果dp[i-1][v-c[i]]。
而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果dp[i][v-c[i]],所以就可以并且必须采用v递增的顺序循环。
多重背包
多重背包(MultiplePack): 有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件...取n[i]件。令dp[i][v]表示前i种物品恰放入一个容量为v的背包的最大价值。
状态转移方程
dp[i][v] = max(dp[i-1][v-k*c[i]]+k*w[i]) (0<=k<=n[i])

浙公网安备 33010602011771号