pubanswer

深入理解01背包问题

SkyDancer2024-06-06

什么是01背包问题?

01背包问题是经典的动态规划问题之一。它的基本描述是:给定一组物品,每个物品有一个重量和一个价值,在限定的总重量内,如何选择物品使得总价值最大。

问题描述

  • 输入:
    • 一个整数数组 weights,表示每个物品的重量
    • 一个整数数组 values,表示每个物品的价值
    • 一个整数 W,表示背包的最大承重
  • 输出:
    • 最大可能的总价值

举个例子

假设有以下物品:

物品 重量 价值
1 2 3
2 3 4
3 4 5
4 5 8

背包最大承重 W = 5。我们可以选择哪些物品使得总价值最大呢?

动态规划解法

状态定义

我们用一个二维数组 dp[i][j] 来表示前 i 个物品在容量为 j 的情况下能够获得的最大价值。

状态转移方程

  • 如果不选第 i 个物品,那么 dp[i][j] = dp[i-1][j]
  • 如果选第 i 个物品,那么 dp[i][j] = dp[i-1][j-weights[i]] + values[i]

综合起来,我们有:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-weights[i]] + values[i])

边界条件

当没有任何物品或者容量为0时,最大价值为0:

dp[0][j] = dp[i][0] = 0

实现代码

def knapsack(weights, values, W):
    n = len(weights)
    dp = [[0 for _ in range(W + 1)] for _ in range(n + 1)]
    
    for i in range(1, n + 1):
        for j in range(1, W + 1):
            if weights[i - 1] <= j:
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1])
            else:
                dp[i][j] = dp[i - 1][j]
    
    return dp[n][W]

# 测试
weights = [2, 3, 4, 5]
values = [3, 4, 5, 8]
W = 5
print(knapsack(weights, values, W)) # 输出: 最大值为8

空间优化

上述解法用了二维数组来存储状态,但其实我们只关心上一行的数据,可以将空间复杂度从 O(nW) 优化到 O(W)。

def knapsack_optimized(weights, values, W):
    n = len(weights)
    dp = [0 for _ in range(W + 1)]
    
    for i in range(n):
        for j in W, weights[i] - 1, -1:
            dp[j] = max(dp[j], dp[j - weights[i]] + values[i])
    
    return dp[W]

# 测试
weights = [2,3,4,5]
values = [3,4,5,8]
W=5
print(knapsack_optimized(weights, values, W)) # 输出: 最大值为8

小结

01背包问题虽然经典,但也非常实用。通过动态规划和空间优化,我们可以高效地解决这个问题。希望这对你有帮助。