动态规划算法--01背包问题详细讲解步骤

举个例子

要确定哪些物品被放入背包以达到最大价值,可以在计算 dp 数组的同时记录选择的物品。具体来说,可以使用一个额外的数组来记录每个状态的选择情况。以下是一个详细的步骤和代码实现:
在这里插入图片描述
在这里插入图片描述

n = 3
W = 5
weights = [2, 1, 3]
values = [6, 3, 5]

初始化 dp 和 choice 数组

dp = [[0] * (W + 1) for _ in range(n + 1)]
choice = [[0] * (W + 1) for _ in range(n + 1)]

print("初始状态:")
print("dp:")
for row in dp:
    print(row)
print("choice:")
for row in choice:
    print(row)

初始状态:
在这里插入图片描述

第1个物品(重量2,价值6)

# 第1个物品
for j in range(1, W + 1):
    if j >= weights[0]:
        if dp[0][j] < dp[0][j - weights[0]] + values[0]:
            dp[1][j] = dp[0][j - weights[0]] + values[0]
            choice[1][j] = 1
        else:
            dp[1][j] = dp[0][j]
    else:
        dp[1][j] = dp[0][j]

print("处理第1个物品后:")
print("dp:")
for row in dp:
    print(row)
print("choice:")
for row in choice:
    print(row)

在这里插入图片描述

第2个物品(重量1,价值3)

# 第2个物品
for j in range(1, W + 1):
    if j >= weights[1]:
        if dp[1][j] < dp[1][j - weights[1]] + values[1]:
            dp[2][j] = dp[1][j - weights[1]] + values[1]
            choice[2][j] = 1
        else:
            dp[2][j] = dp[1][j]
    else:
        dp[2][j] = dp[1][j]

print("处理第2个物品后:")
print("dp:")
for row in dp:
    print(row)
print("choice:")
for row in choice:
    print(row)

在这里插入图片描述

第3个物品(重量3,价值5)

for j in range(1, W + 1):
    if j >= weights[2]:
        if dp[2][j] < dp[2][j - weights[2]] + values[2]:
            dp[3][j] = dp[2][j - weights[2]] + values[2]
            choice[3][j] = 1
        else:
            dp[3][j] = dp[2][j]
    else:
        dp[3][j] = dp[2][j]

print("处理第3个物品后:")
print("dp:")
for row in dp:
    print(row)
print("choice:")
for row in choice:
    print(row)

在这里插入图片描述

回溯路径

在这里插入图片描述
在这里插入图片描述

# 回溯路径,确定选择的物品
selected_items = []
j = W
for i in range(n, 0, -1):
    if choice[i][j] == 1:
        selected_items.append(i - 1)  # 物品编号从0开始
        j -= weights[i-1]

# 返回最大价值和选择的物品
max_value = dp[n][W]
selected_items = selected_items[::-1]

print(f"最大价值: {max_value}")
print(f"选择的物品: {selected_items}")

在这里插入图片描述
最终,选择的物品是第2个(编号1)和第3个(编号2)物品,最大价值为11。

完整代码

def knapsack_with_items(n, W, weights, values):
    # 初始化 dp 和 choice 数组
    dp = [[0] * (W + 1) for _ in range(n + 1)]
    choice = [[0] * (W + 1) for _ in range(n + 1)]
    
    # 填充 dp 和 choice 数组
    for i in range(1, n + 1):
        for j in range(1, W + 1):
            if j >= weights[i-1]:
                if dp[i-1][j] < dp[i-1][j-weights[i-1]] + values[i-1]:
                    dp[i][j] = dp[i-1][j-weights[i-1]] + values[i-1]
                    choice[i][j] = 1
                else:
                    dp[i][j] = dp[i-1][j]
            else:
                dp[i][j] = dp[i-1][j]
    
    # 回溯路径,确定选择的物品
    selected_items = []
    j = W
    for i in range(n, 0, -1):
        if choice[i][j] == 1:
            selected_items.append(i - 1)  # 物品编号从0开始
            j -= weights[i-1]
    
    # 返回最大价值和选择的物品
    return dp[n][W], selected_items[::-1]

# 示例
n = 3
W = 5
weights = [2, 1, 3]
values = [6, 3, 5]
max_value, selected_items = knapsack_with_items(n, W, weights, values)
print(f"最大价值: {max_value}")
print(f"选择的物品: {selected_items}")

一维数组的栗子

使用一维数组可以优化空间复杂度,将空间复杂度从O(nW) 降低到 O(W)。我们可以通过滚动数组的方式来实现这一点。
在这里插入图片描述

注意:从后往前遍历背包容量

如果背包容量j从前往后会出现什么问题呢?

n = 3
W = 5
weights = [2, 1, 3]
values = [6, 3, 5]

# 初始化 dp 数组
dp = [0] * (W + 1)

print("初始状态:")
print("dp:", dp)

# 第1个物品
for j in range(weights[0], W + 1):
    if dp[j] < dp[j - weights[0]] + values[0]:
        dp[j] = dp[j - weights[0]] + values[0]

print("处理第1个物品后:")
print("dp:", dp)

在这里插入图片描述
从前往后遍历背包容量 j 会导致同一个物品被多次选择,从而违反了01背包问题的约束。例如,在处理第1个物品时,当 j=4 时,dp[4] 被更新为 dp[2] + 6,而 dp[2] 已经被更新过,导致物品1被重复使用。

初始化

在这里插入图片描述

第1个物品(重量2,价值6)

for j in range(W, weights[0] - 1, -1):
    if dp[j] < dp[j - weights[0]] + values[0]:
        dp[j] = dp[j - weights[0]] + values[0]
        choice[1][j] = 1

print("处理第1个物品后:")
print("dp:", dp)
print("choice:")
for row in choice:
    print(row)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第2个物品(重量1,价值3)

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

第3个物品(重量3,价值5)

在这里插入图片描述

回溯路径

在这里插入图片描述

完整代码

def knapsack_with_items_one_dim(n, W, weights, values):
    # 初始化 dp 数组
    dp = [0] * (W + 1)
    # 初始化 choice 数组,用于记录选择的物品
    choice = [[0] * (W + 1) for _ in range(n + 1)]
    
    # 填充 dp 和 choice 数组
    for i in range(1, n + 1):
        for j in range(W, weights[i-1] - 1, -1):
            if dp[j] < dp[j - weights[i-1]] + values[i-1]:
                dp[j] = dp[j - weights[i-1]] + values[i-1]
                choice[i][j] = 1
    
    # 回溯路径,确定选择的物品
    selected_items = []
    j = W
    for i in range(n, 0, -1):
        if choice[i][j] == 1:
            selected_items.append(i - 1)  # 物品编号从0开始
            j -= weights[i-1]
    
    # 返回最大价值和选择的物品
    return dp[W], selected_items[::-1]

# 示例
n = 3
W = 5
weights = [2, 1, 3]
values = [6, 3, 5]
max_value, selected_items = knapsack_with_items_one_dim(n, W, weights, values)
print(f"最大价值: {max_value}")
print(f"选择的物品: {selected_items}")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值