动态规划——01背包问题

基本思想:

动态规划与分治法类似,其基本思想也是将待求问题,分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。而与分治法不同的是,对子问题之间重叠的部分,分治法会重复地求解这些重叠的部分,而动态规划只会对它们求解一次,然后用表格保存这些解,如此,就可以很大程度地避免重复计算。

解题思路:

1. 存储输入数据

显然输入的是二维的数据,应该用二维数组来存储。当然,还有背包容量的存储

# 读取输入
capacity = int(input())
items = []
for i in range(5):
    line = input().strip()
    if line:
        parts = list(map(int, line.split()))
        # line.split() 按空格分隔字符串得到字符串列表
        # map(int, ...):将每个字符串转换为整数
        # list(...):将结果转换为列表 从而实现双重列表
        if len(parts) == 3:
            num, weight, value = parts
            items.append((num, weight, value))
n = len(items)  # 物体的总数

# 可有可无
if n == 0:
    print("没有数据输入")
    exit()

这里因为只有5组输入,所以使用了for循环进行输入,而实际上这样代码的适用性一般,以下是改进。改进后就可以实现随机组数据的输入。

# 读取输入
capacity = int(input())
items = []
while True:
    try:
        line = input().strip()
        if line:
            parts = list(map(int, line.split()))
            if len(parts) == 3:
                num, weight, value = parts
                items.append((num, weight, value))  # 格式:(编号,重量,价值)
    except EOFError:
        break
n = len(items)  # 物体的总数

# 可有可无
if n == 0:
    print("没有输入数据")
    exit()

# line.split() 按空格分隔字符串得到字符串列表
# map(int, ...):将每个字符串转换为整数
# list(...):将结果转换为列表 从而实现双重列表

2.初始化动态规划表

其实就是创建一个二维数组,初值都先赋为0

# 初始化动态规划表
dp = [[0] * (capacity + 1) for _ in range(n + 1)] #创建一个二维数组 dp,维度为 (n+1) x (capacity+1),初始值全为0

3.填充动态规划表

重难点!!也是关键点

1. 动态规划表的定义

dp[i][w] 表示:考虑前i个物品,在背包容量为w时能获得的最大价值。

2. 填表的基本原理

表格填充遵循以下决策逻辑:

•  不选当前物品:价值继承自上一行同列的值  

•  选当前物品:当前物品价值 + 剩余容量的最优解

3. 具体填表过程解析

for i in range(1, n + 1):          # 逐个考虑物品
    weight, value, _ = items[i-1]  # 获取当前物品属性
    for w in range(capacity + 1):  # 遍历所有可能容量
        if weight > w:
            dp[i][w] = dp[i-1][w]  # 情况1:放不下
        else:
            # 情况2:选择是否放入
            dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight] + value)
深度解析:

1. 情况1很容易理解,放不下,那就顺延之前的方案

2.而情况2,选择是否放入,这一部分我们利用例子来说明

理解误区:

这里的动态规划表事实上还是将每种可能性都有计算

所以这里的dp[i-1][w-weight]+value其实是已知的,这样才能计算出数据与不拿的情况相比较

从而取其最大值

4.回溯选中的物品 

注意:是从后向前选择,所以后面要反转得到正确顺序

selected = []   # 用来存储选中的物品的编号
current_weight = capacity
for i in range(n, 0, -1):
    if dp[i][current_weight] > dp[i-1][current_weight]: # 二者不相等,说明物品有放入背包
        item = items[i-1]
        selected.append(item[0])
        current_weight -= item[1]  # 减去已选中物体的重量

着重关注第一和第二点(虽然比喻有点抽象)

5.输出结果

# 反转得到正确顺序,因为之前是倒取的
selected.reverse()

# 输出结果
#将selected转换为字符串后输出,然后用join连接
print(' '.join(map(str, selected)))

汇总实现

1. 已知输入数据量版

# 读取输入
capacity = int(input())
items = []
for i in range(5):
    line = input().strip()
    if line:
        parts = list(map(int, line.split()))
        # line.split() 按空格分隔字符串得到字符串列表
        # map(int, ...):将每个字符串转换为整数
        # list(...):将结果转换为列表 从而实现双重列表
        if len(parts) == 3:
            num, weight, value = parts
            items.append((num, weight, value))
n = len(items)
# 可有可无
if n == 0:
    print("")
    exit()

# 初始化动态规划表
dp = [[0] * (capacity + 1) for _ in range(n + 1)] #创建一个二维数组 dp,维度为 (n+1) x (capacity+1),初始值全为0

for i in range(1, n + 1):
    num, weight, value = items[i-1]
    for w in range(capacity + 1):
        if weight > w:   # 如果太重,就不放入
            dp[i][w] = dp[i-1][w]
        else:  # 如果可以放入,那就判断是选择放还是不放
            dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight] + value)

# 回溯选中的物品 注意:是从后向前选择,所以后面要反转得到正确顺序
selected = []   # 用来存储选中的物品的编号
current_weight = capacity
for i in range(n, 0, -1):
    if dp[i][current_weight] > dp[i-1][current_weight]: # 二者不相等,说明物品有放入背包
        item = items[i-1]
        selected.append(item[0])
        current_weight -= item[1]  # 减去已选中物体的重量

# 反转得到正确顺序
selected.reverse()

# 输出结果
#将selected转换为字符串后输出,然后用join连接
print(' '.join(map(str, selected)))

2.泛用版

# 读取输入
capacity = int(input())
items = []
while True:
    try:
        line = input().strip()
        if line:
            parts = list(map(int, line.split()))
            if len(parts) == 3:
                num, weight, value = parts
                items.append((weight, value, num))  # 统一使用 (重量, 价值, 编号) 结构
    except EOFError:
        break

n = len(items)
if n == 0:
    print("")
    exit()

# 初始化动态规划表
dp = [[0] * (capacity + 1) for _ in range(n + 1)]

# 填充动态规划表
for i in range(1, n + 1):
    weight, value, num = items[i-1]  # 现在解包顺序正确
    for w in range(capacity + 1):
        if weight > w:
            dp[i][w] = dp[i-1][w]
        else:
            dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight] + value)

# 回溯选中的物品
selected = []
current_weight = capacity
for i in range(n, 0, -1):
    if dp[i][current_weight] > dp[i-1][current_weight]:
        weight, value, num = items[i-1]  # 正确解包
        selected.append(num)  # 添加编号
        current_weight -= weight  # 减去重量

# 反转得到正确顺序
selected.reverse()

# 输出结果
print(' '.join(map(str, selected)))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值