【动态规划算法】【Python实现】0-1背包

问题描述

  • 给定 n n n种物品和一背包,物品 i i i的重量是 w i w_{i} wi,其价值为 v i v_{i} vi,背包的容量为 c c c
  • 如何选择装入背包中的物品,使得装入背包中物品的总价值最大
形式化描述
  • 给定 c > 0 c > 0 c>0 w i > 0 w_{i} > 0 wi>0 v i > 0 ( 1 ≤ i ≤ n ) v_{i} > 0 (1 \leq i \leq n) vi>0(1in),找出一个 n n n 0 − 1 0-1 01向量 ( x 1 , x 2 , ⋯   , x n ) (x_{1} , x_{2} , \cdots , x_{n}) (x1,x2,,xn) x i ∈ {   0 , 1   } ( 1 ≤ i ≤ n ) x_{i} \in \set{0 , 1} (1 \leq i \leq n) xi{0,1}(1in),使得 ∑ i = 1 n w i x i ≤ c \displaystyle\sum\limits_{i = 1}^{n}{w_{i} x_{i}} \leq c i=1nwixic,而且 ∑ i = 1 n v i x i \displaystyle\sum\limits_{i = 1}^{n}{v_{i} x_{i}} i=1nvixi达到最大
  • 0 − 1 0-1 01背包问题是一个特殊的整数规划问题

max ⁡ ∑ i = 1 n v i x i { ∑ i = 1 n w i x i ≤ c x i ∈ {   0 , 1   } ( 1 ≤ i ≤ n ) \max\displaystyle\sum\limits_{i = 1}^{n}{v_{i} x_{i}} \kern{2em} \begin{cases} \displaystyle\sum\limits_{i = 1}^{n}{w_{i} x_{i} \leq c} \\ x_{i} \in \set{0 , 1} (1 \leq i \leq n) \end{cases} maxi=1nvixi i=1nwixicxi{0,1}(1in)


最优子结构性质

  • ( y 1 , y 2 , ⋯   , y n ) (y_{1} , y_{2} , \cdots , y_{n}) (y1,y2,,yn)是所给 0 − 1 0-1 01背包问题的一个最优解,则 ( y 2 , y 3 , ⋯   , y n ) (y_{2} , y_{3} , \cdots , y_{n}) (y2,y3,,yn)是下面相应子问题的一个最优解

max ⁡ ∑ i = 2 n v i x i { ∑ i = 2 n w i x i ≤ c − w 1 y 1 x i ∈ {   0 , 1   } ( 2 ≤ i ≤ n ) \max\displaystyle\sum\limits_{i = 2}^{n}{v_{i} x_{i}} \kern{2em} \begin{cases} \displaystyle\sum\limits_{i = 2}^{n}{w_{i} x_{i}} \leq c - w_{1} y_{1} \\ x_{i} \in \set{0 , 1} (2 \leq i \leq n) \end{cases} maxi=2nvixi i=2nwixicw1y1xi{0,1}(2in)

  • 否则,设 ( z 2 , z 3 , ⋯   , z n ) (z_{2} , z_{3} , \cdots , z_{n}) (z2,z3,,zn)是上述子问题的一个最优解,而 ( y 2 , y 3 , ⋯   , y n ) (y_{2} , y_{3} , \cdots , y_{n}) (y2,y3,,yn)不是它的最优解,由此可知, ∑ i = 2 n v i z i > ∑ i = 2 n v i y i \displaystyle\sum\limits_{i = 2}^{n}{v_{i} z_{i}} > \displaystyle\sum\limits_{i = 2}^{n}{v_{i} y_{i}} i=2nvizi>i=2nviyi,且 w 1 y 1 + ∑ i = 2 n w i z i ≤ c w_{1} y_{1} + \displaystyle\sum\limits_{i = 2}^{n}{w_{i} z_{i}} \leq c w1y1+i=2nwizic,因此 v 1 y 1 + ∑ i = 2 n v i z i > ∑ i = 1 n v i y i v_{1} y_{1} + \displaystyle\sum\limits_{i = 2}^{n}{v_{i} z_{i}} > \displaystyle\sum\limits_{i = 1}^{n}{v_{i} y_{i}} v1y1+i=2nvizi>i=1nviyi,说明 ( y 1 , z 2 , ⋯   , z n ) (y_{1} , z_{2} , \cdots , z_{n}) (y1,z2,,zn)是所给 0 − 1 0-1 01背包问题的一个更优解,从而 ( y 1 , y 2 , ⋯   , y n ) (y_{1} , y_{2} , \cdots , y_{n}) (y1,y2,,yn)不是所给 0 − 1 0-1 01背包问题的最优解,此为矛盾

递归关系

  • 设所给 0 − 1 0-1 01背包问题的子问题 max ⁡ ∑ k = i n v k x k { ∑ k = i n w k x k ≤ j x k ∈ {   0 , 1   } ( i ≤ k ≤ n ) \max\displaystyle\sum\limits_{k = i}^{n}{v_{k} x_{k}} \kern{2em} \begin{cases} \displaystyle\sum\limits_{k = i}^{n}{w_{k} x_{k} \leq j} \\ x_{k} \in \set{0 , 1} (i \leq k \leq n) \end{cases} maxk=invkxk k=inwkxkjxk{0,1}(ikn)的最优值为 m ( i , j ) m(i , j) m(i,j),即 m ( i , j ) m(i , j) m(i,j)是背包容量为 j j j,可选择物品为 i i i i + 1 i + 1 i+1 ⋯ \cdots n n n 0 − 1 0-1 01背包问题的最优值
m ( i , j ) m(i , j) m(i,j)递归方程

m ( i , j ) = { max ⁡ {   m ( i + 1 , j ) , m ( i + 1 , j − w i ) + v i   } , j ≥ w i m ( i + 1 , j ) , 0 ≤ j < w i m(i , j) = \begin{cases} \max\set{m(i + 1 , j) , m(i + 1 , j - w_{i}) + v_{i}} , & j \geq w_{i} \\ m(i + 1 , j) , & 0 \leq j < w_{i} \end{cases} m(i,j)={max{m(i+1,j),m(i+1,jwi)+vi},m(i+1,j),jwi0j<wi

m ( n , j ) = { v n , j ≥ w n 0 , 0 ≤ j < w n m(n , j) = \begin{cases} v_{n} , & j \geq w_{n} \\ 0 , & 0 \leq j < w_{n} \end{cases} m(n,j)={vn,0,jwn0j<wn


Python实现

def knapsack(weights, values, capacity):
    n = len(weights)

    # 创建一个二维数组用于保存子问题的解
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]

    # 填充二维数组
    for i in range(1, n + 1):
        for j in range(1, capacity + 1):
            # 当前物品的重量大于背包容量, 无法放入背包
            if weights[i - 1] > j:
                dp[i][j] = dp[i - 1][j]
            # 考虑将当前物品放入背包和不放入背包两种情况, 选择价值最大的方案
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1])

    # 构造最优解
    selected_items = []

    i, j = n, capacity
    while i > 0 and j > 0:
        # 当前物品被选中
        if dp[i][j] > dp[i - 1][j]:
            selected_items.append(i - 1)

            j -= weights[i - 1]

        i -= 1

    selected_items.reverse()

    return dp[n][capacity], selected_items


weights = [2, 3, 4, 5]
values = [3, 4, 5, 6]
capacity = 8

max_value, selected_items = knapsack(weights, values, capacity)

print(f'最大价值为: {max_value}')
print(f'选中的物品索引为: {selected_items}')

  • 24
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值