0-1 背包问题——动态规划(一)

1. 问题描述

N N N 种物品,物品 i i i 的重量为 w i w_i wi,价格为 v i v_i vi,背包所能容纳的最大重量为 W W W,求背包内装入物品总价值的最大值。其中, N , W , w i , v i ≥ 0 N, W, w_i, v_i \geq 0 N,W,wi,vi0

若每种物品仅有一件,即每种物品的状态为 x i x_i xi x i ∈ { 0 , 1 } x_i \in \{0,1\} xi{0,1},则称该问题为0-1 背包问题

2. 数学描述

约束条件:
(1) { ∑ i = 1 N w i x i ≤ W x i ∈ { 0 , 1 }      ( 1 ≤ i ≤ N ) \left\{\begin{aligned} & \sum_{i=1}^N w_ix_i \leq W \tag{1} \\ & x_i \in \{0,1\} \ \ \ \ (1 \leq i \leq N) \end{aligned} \right. i=1NwixiWxi{0,1}    (1iN)(1)

目标函数:
(2) ∑ i = 1 N v i x i \begin{aligned} & \sum_{i=1}^N v_ix_i \tag{2} \end{aligned} i=1Nvixi(2)

该问题可以描述为:求满足约束条件 (1),同时使目标函数 (2) 最大的解向量 X = ( x 1 , x 2 , ⋯   , x N ) X = (x_1, x_2, \cdots ,x_N) X=(x1,x2,,xN)

3. 解题思路

0-1 背包是一类经典的动态规划问题,求解关键在于推倒递推公式。

  • N N N 为物品总数, W W W 为背包最大容纳重量,定义一个规模为 N ∗ ( W + 1 ) N * (W+1) N(W+1) 的二维数组。这里以 N = 5 N = 5 N=5 W = 10 W =10 W=10 为例说明。

  • d p ( i , j ) dp(i,j) dp(i,j) 表示可选前 i i i 件物品,在背包容量为 j j j 时,背包内装入物品的最大值 。

  • 自上向下,自左向右,填充该二维数组。

  1. 第一行,可选物品仅为 ①。

d p ( 1 , j ) = { 0      j ∈ [ 0 , 2 ) 6      j ∈ [ 2 , 10 ] dp(1,j)= \left\{\begin{aligned} & 0 \ \ \ \ j \in [0,2) \\ & 6 \ \ \ \ j \in [2,10] \end{aligned} \right. dp(1,j)={0    j[0,2)6    j[2,10]

i w i w_i wi v i v_i vi012345678910
12600666666666
223
365
454
546
  1. 第二行,可选物品为 ①②,可借助第一行结果进行计算。这两行的区别仅在于物品 ② 是否应该放入,有两种情况:
    (1) 不放入物品 ②,背包总价值不变,即 d p ( 2 , j ) = d p ( 1 , j ) dp(2,j) = dp(1,j) dp(2,j)=dp(1,j)
    (2) 放入物品 ②,背包应保证足够剩余容量,也就是在 d p ( 1 , j − w 2 ) dp(1,j-w_2) dp(1,jw2) 的基础上放入,即 d p ( 2 , j ) = d p ( 1 , j − w 2 ) + v 2 dp(2,j) = dp(1,j-w_2) + v_2 dp(2,j)=dp(1,jw2)+v2

二者取最大值,即 d p ( 2 , j ) = m a x ( d p ( 1 , j ) , d p ( 1 , j − w 2 ) + v 2 ) dp(2,j) = max(dp(1,j),dp(1,j-w_2) + v_2) dp(2,j)=max(dp(1,j),dp(1,jw2)+v2)

i w i w_i wi v i v_i vi012345678910
12600666666666
22300669999999
365
454
546
  1. i i i 行,可选物品为 { 1 , ⋯   , i } \{1, \cdots ,i\} {1,,i},借助第 i − 1 i-1 i1 行结果进行计算。这两行的区别仅在于物品 i i i 是否应该放入,有两种情况:
    (1) 不放入物品 i i i,背包总价值不变,即 d p ( i , j ) = d p ( i − 1 , j ) dp(i,j) = dp(i-1,j) dp(i,j)=dp(i1,j)
    (2) 放入物品 i i i,背包应保证足够剩余容量,也就是在 d p ( i − 1 , j − w i ) dp(i-1,j-w_i) dp(i1,jwi) 的基础上放入,即 d p ( i , j ) = d p ( i − 1 , j − w i ) + v i dp(i,j) = dp(i-1,j-w_i) + v_i dp(i,j)=dp(i1,jwi)+vi

二者取最大值,即 d p ( i , j ) = m a x ( d p ( i − 1 , j ) , d p ( i − 1 , j − w i ) + v i ) dp(i,j) = max(dp(i-1,j),dp(i-1,j-w_i) + v_i) dp(i,j)=max(dp(i1,j),dp(i1,jwi)+vi)

i w i w_i wi v i v_i vi012345678910
12600666666666
22300669999999
36500669999111114
454006699910111314
5460066991212151515

综上,状态转移方程为:

(1) d p ( 1 , j ) = { 0      0 ≤ j &lt; w 1 v 1      j ≥ w 1 dp(1,j)= \left\{\begin{aligned} &amp; 0 \ \ \ \ 0 \leq j &lt; w_1 \tag{1} \\ &amp; v_1 \ \ \ \ j \geq w_1 \end{aligned} \right. dp(1,j)={0    0j<w1v1    jw1(1)

(2) d p ( i , j ) = { d p ( i − 1 , j )      0 ≤ j &lt; w i m a x ( d p ( i − 1 , j ) , d p ( i − 1 , j − w i ) + v i )      j ≥ w i dp(i,j)= \left\{\begin{aligned} &amp; dp(i-1,j) \ \ \ \ 0 \leq j &lt; w_i \tag{2} \\ &amp; max(dp(i-1,j),dp(i-1,j-w_i) + v_i) \ \ \ \ j \geq w_i \end{aligned} \right. dp(i,j)={dp(i1,j)    0j<wimax(dp(i1,j),dp(i1,jwi)+vi)    jwi(2)

求解过程就是填充规模为 { N ∗ ( W + 1 ) } \{N * (W+1)\} {N(W+1)} 的二维数组,每次填充仅需 O ( 1 ) \mathcal{O}(1) O(1) 时间,故算法的时间复杂度为 O ( N W ) \mathcal{O}(NW) O(NW)

动态规划特点总结

  1. 问题分解。将大问题转化为子问题,借助子问题的结果求解大问题。

  2. 状态转移。每个子问题或者子子问题的求解结果都视为一个状态,状态转移过程即问题的转化、复合过程。

  3. 记忆化搜索。用空间换时间,存储子问题的结果作为后续大问题求解的已知条件。

4. 算法优化

以上过程存在空间优化潜力,计算第 i i i 行时,仅用到了第 i + 1 i+1 i+1 行结果。存储空间可以由二维数组 N ∗ ( W + 1 ) N*(W+1) N(W+1) 压缩为一维数组 W + 1 W+1 W+1

状态更新过程应采用逆序遍历,即 j j j 从最大值开始,从大到小更新数组。若采用顺序更新, d p ( j − w i ) dp(j-w_i) dp(jwi) 更新在前、使用在后,导致 i − 1 i-1 i1 行结果在未被使用之前就被覆盖了。

状态转移方程为,

d p ( j ) = { m a x ( d p ( j ) , d p ( j − w i ) + v i )      j ≥ w i d p ( j )      0 ≤ j &lt; w i dp(j)= \left\{\begin{aligned} &amp; max(dp(j),dp(j-w_i) + v_i) \ \ \ \ j \geq w_i \\ &amp; dp(j) \ \ \ \ 0 \leq j &lt; w_i \end{aligned} \right. dp(j)={max(dp(j),dp(jwi)+vi)    jwidp(j)    0j<wi

5. 问题拓展

以上问题的约束条件为,

(1) ∑ i = 1 N w i x i ≤ W \begin{aligned} \sum_{i=1}^N w_ix_i \leq W \tag{1} \end{aligned} i=1NwixiW(1)

(2) x i ∈ { 0 , 1 }      ( 1 ≤ i ≤ N ) \begin{aligned} x_i \in \{0,1\} \ \ \ \ (1 \leq i \leq N) \tag{2} \end{aligned} xi{0,1}    (1iN)(2)

  • 约束条件(1),要求物品总重量不得超过背包容量,允许未装满。约束条件变为要求总重量恰好等于背包容量,拓展为背包装满问题

  • 约束条件(2),要求每种物品仅有一件。约束条件变为物品件数不限,拓展为第二类问题完全背包问题

6. 典型例题

### 关于头歌平台第3关0-1背包问题动态规划解题思路 #### 什么是0-1背包问题0-1背包问题是典型的动态规划问题之,其核心在于给定组物品及其重量和价值,在不超过背包总承重的前提下最大化所选物品的价值。每个物品只能选择次或者不选择。 对于该问题的核心状态转移方程可以表示为: \[ dp[j] = \max(dp[j], dp[j-w_i]+v_i) \] 其中 \( w_i \) 和 \( v_i \) 分别代表第 \( i \) 件物品的重量和价值,\( j \) 表示当前考虑的背包容量[^1]。 #### 头歌平台第3关的具体描述 虽然未直接提及具体题目细节,但通常此类问题会涉及如下条件: - 给定若干物品以及它们对应的重量和价值; - 背包的最大承载能力; - 输出能够获得的最大价值。 基于上述背景,以下是解决此问题的关键步骤: #### 动态规划的状态定义与初始化 设二维数组 `dp[i][j]` 表示从前 \( i \) 个物品中选取部分放入容量为 \( j \) 的背包所能得到的最大价值,则初始状态应满足以下两点: - 当没有任何物品可选时 (\( i=0 \)) 或者背包容量为零 (\( j=0 \)) ,此时最大价值均为零: \[ dp[0][j]=0, \quad dp[i][0]=0 \] 随后按照递推关系逐步填充整个表格直至求得最终结果。 #### 状态转移逻辑实现 如果当前处理的是第 \( i \) 项商品,并且它的体积小于等于剩余空间大小 \( j \),则有两种可能的选择——要么将其加入背囊;要么舍弃它继续考察其他选项。因此更新规则如下所示: ```python if weight[i] <= j: dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]) else: dp[i][j] = dp[i-1][j] ``` 为了节省内存消耗,还可以采用维滚动数组的方式简化存储结构,只需注意遍历顺序需从高至低以防止覆盖尚未使用的旧数据[^2]。 #### 边界情况讨论 需要注意些特殊情况下的行为表现,比如当所有物品都无法放进背包里时返回的结果应该是多少?又或者是存在负数权重的情况该如何处理? --- ### 示例代码展示 下面是利用Python编写的简单版本程序来演示如何计算最优解的过程: ```python def knapsack_01(values, weights, capacity): n = len(values) # 创建DP表并初始化 dp = [0]*(capacity+1) for i in range(n): # 对每件物品进行迭代 for c in range(capacity, weights[i]-1, -1): # 倒序遍历容量 if weights[i] <= c: dp[c] = max(dp[c], dp[c - weights[i]] + values[i]) return dp[-1] # 测试样例 values = [60, 100, 120] weights = [10, 20, 30] W = 50 print(knapsack_01(values, weights, W)) ``` 以上就是针对头歌平台第三关有关0-1背包问题种通用解决方案概述[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值