📜  0-1 背包 (1)

📅  最后修改于: 2023-12-03 15:13:04.633000             🧑  作者: Mango

0-1 背包算法

简介

0-1 背包算法是一个经典的算法问题,通常用于解决背包问题。背包问题可以描述为:假设有一个背包,容量为 W,有 n 个物品和它们各自的重量和价值。问题要求选择一些物品放入背包中,使得在背包容量范围内能放入的物品中,价值最大。

0-1 背包算法的名称中的 0-1 表示每个物品只能选择一次(放或不放)。

算法过程

0-1 背包算法的基本思路是:将问题分解成子问题,然后使用递归或动态规划的方式求解。

递归

假设我们已经有了一个函数 $F(n, W)$,它的作用是求解前 n 个物品放入容量为 W 的背包中,能够得到的最大价值。不妨假设第 n 个物品的重量为 w[n],价值为 v[n]。

  • 如果我们选择不放第 n 个物品,那么问题就转化为求解前 n-1 个物品放入容量为 W 的背包中的最大价值,即 $F(n-1, W)$;
  • 如果我们选择放第 n 个物品,那么问题就转化为求解前 n-1 个物品放入容量为 W-w[n] 的背包中的最大价值并加上第 n 个物品的价值,即 $F(n-1, W-w[n])+v[n]$。

综上所述,我们可以得到递归公式:

$$F(n, W) = \begin{cases} 0 & \text{if } n=0 \text{ or } W=0 \ F(n-1, W) & \text{if } w[n]>W \ \max(F(n-1, W), F(n-1, W-w[n])+v[n]) & \text{if } w[n]\leqslant W \end{cases}$$

动态规划

递归虽然能够求解 0-1 背包问题,但是存在许多重复计算的情况,因此效率较低。我们可以使用动态规划的方式来优化算法。

假设 $f[i][j]$ 表示前 i 个物品放入容量为 j 的背包中所能获得的最大价值,那么我们可以得到状态转移方程:

$$f[i][j] = \begin{cases} 0 & \text{if } i=0 \text{ or } j=0 \ f[i-1][j] & \text{if } w[i]>j \ \max(f[i-1][j], f[i-1][j-w[i]]+v[i]) & \text{if } w[i]\leqslant j \end{cases}$$

由此,我们可以得到动态规划的算法流程:

def knapsack(n, W, w, v):
    f = [[0 for j in range(W+1)] for i in range(n+1)]
    for i in range(1, n+1):
        for j in range(1, W+1):
            if w[i] > j:
                f[i][j] = f[i-1][j]
            else:
                f[i][j] = max(f[i-1][j], f[i-1][j-w[i]]+v[i])
    return f[n][W]
时间复杂度

递归算法的时间复杂度为 $O(2^n)$,因为每个物品都有放或不放两种情况。而动态规划算法的时间复杂度为 $O(nW)$,其中 n 表示物品个数,W 表示背包容量。因此,动态规划算法相比递归算法具有较高的效率。