Python 背包问题

✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。
🍎个人主页:小嗷犬的个人主页
🍊个人网站:小嗷犬的技术小站
🥭个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。



背包问题

背包问题Knapsack Problem)是一类常见的组合优化问题。其问题描述为:给定一个固定大小、能够携重 W W W 的背包,以及一组有价值和重量的物品,找出一个最佳解决方案,使得装入背包的物品总重量不超过 W W W,且总价值最大。

背包问题

通常情况下,背包问题可以分为以下三类:

  • 0-1 背包问题:每种物品仅有一件,可以选择放或不放。
  • 完全背包问题:每种物品有无限件,可以选择放多少件或不放。
  • 多重背包问题:每种物品有 n i n_i ni 件,可以选择放多少件或不放。

本文将介绍如何使用 Python 解决以上三类背包问题。


0-1 背包问题

0-1 背包问题0-1 Knapsack Problem)是最基础的背包问题。其问题描述为:给定一个固定大小、能够携重 W W W 的背包,以及 N N N 个价值、重量分别为 v i v_i vi w i w_i wi 的物品,找出一个最佳解决方案,使得装入背包的物品总重量不超过 W W W,且总价值最大。

有一个容量为 10 10 10 的背包,现有 4 4 4 个物品,其价值和重量分别为:

物品1234
价值1359
重量2347

求背包能装下的最大价值以及取得最大价值时的物品组合。

N = 4  # 物品数量
W = 10  # 背包容量
v = [0, 1, 3, 5, 9]  # 物品价值
w = [0, 2, 3, 4, 7]  # 物品重量
dp = [[0] * (W + 1) for _ in range(N + 1)]  # dp[i][j] 表示前 i 个物品放入容量为 j 的背包的最大价值
flag = [
    [0] * (W + 1) for _ in range(N + 1)
]  # flag[i][j] 表示前 i 个物品放入容量为 j 的背包最大价值时装入物品的最大编号

# dp 求解最大价值并更新 flag
for i in range(1, N + 1):
    for j in range(1, W + 1):
        if j < w[i]:
            dp[i][j] = dp[i - 1][j]
        elif dp[i - 1][j] > dp[i - 1][j - w[i]] + v[i]:
            dp[i][j] = dp[i - 1][j]
            flag[i][j] = flag[i - 1][j]
        else:
            dp[i][j] = dp[i - 1][j - w[i]] + v[i]
            flag[i][j] = i - 1
ans = dp[N][W]

# 追踪解方案
sol = [0] * N
while flag[N][W] != 0:
    temp = flag[N][W]
    sol[temp] = 1
    W -= w[temp]
    N = temp - 1

# 输出结果
print(f"最大价值为:{ans}")
print(f"取得最大价值时的物品组合为:{sol}")

结果

最大价值为:12
取得最大价值时的物品组合为:[0, 1, 0, 1]

完全背包问题

完全背包问题Unbounded Knapsack Problem)是背包问题的一种变种。其问题描述为:给定一个固定大小、能够携重 W W W 的背包,以及 N N N 个价值、重量分别为 v i v_i vi w i w_i wi 的物品,每种物品有无限件,可以选择放多少件或不放,找出一个最佳解决方案,使得装入背包的物品总重量不超过 W W W,且总价值最大。

有一个容量为 15 15 15 的背包,现有 4 4 4 个物品,其价值和重量分别为:

物品1234
价值1359
重量2347

求背包能装下的最大价值以及取得最大价值时的物品组合。

N = 4  # 物品数量
W = 15  # 背包容量
v = [0, 1, 3, 5, 9]  # 物品价值
w = [0, 2, 3, 4, 7]  # 物品重量
dp = [[0] * (W + 1) for _ in range(N + 1)]  # dp[i][j] 表示前 i 个物品放入容量为 j 的背包的最大价值
flag = [
    [0] * (W + 1) for _ in range(N + 1)
]  # flag[i][j] 表示前 i 个物品放入容量为 j 的背包最大价值时装入物品的最大编号

# dp 求解最大价值并更新 flag
for i in range(1, N + 1):
    for j in range(1, W + 1):
        if j >= w[i]:
            if dp[i - 1][j] < dp[i][j - w[i]] + v[i]:
                dp[i][j] = dp[i][j - w[i]] + v[i]
                flag[i][j] = i
            else:
                dp[i][j] = dp[i - 1][j]
                flag[i][j] = flag[i - 1][j]
        else:
            dp[i][j] = dp[i - 1][j]
            flag[i][j] = flag[i - 1][j]
ans = dp[N][W]

# 追踪解方案
sol = [0] * N
while flag[N][W] != 0:
    N = flag[N][W]
    sol[N - 1] = 1
    W -= w[N]
    while flag[N][W] == N:
        W = W - w[N]
        sol[N - 1] += 1

# 输出结果
print(f"最大价值为:{ans}")
print(f"取得最大价值时的物品组合为:{sol}")

结果

最大价值为:19
取得最大价值时的物品组合为:[0, 0, 2, 1]

多重背包问题

多重背包问题Bounded Knapsack Problem)是背包问题的一种变种。其问题描述为:给定一个固定大小、能够携重 W W W 的背包,以及 N N N 个价值、重量分别为 v i v_i vi w i w_i wi 的物品,每种物品有 n i n_i ni 件,可以选择放多少件或不放,找出一个最佳解决方案,使得装入背包的物品总重量不超过 W W W,且总价值最大。

有一个容量为 25 25 25 的背包,现有 4 4 4 个物品,其价值、重量、数量分别为:

物品1234
价值1359
重量2347
数量5432

求背包能装下的最大价值以及取得最大价值时的物品组合。

N = 4  # 物品数量
W = 25  # 背包容量
v = [0, 1, 3, 5, 9]  # 物品价值
w = [0, 2, 3, 4, 7]  # 物品重量
n = [0, 5, 4, 3, 2]  # 物品数量
dp = [[0] * (W + 1) for _ in range(N + 1)]  # dp[i][j] 表示前 i 个物品放入容量为 j 的背包的最大价值
flag = [
    [0] * (W + 1) for _ in range(N + 1)
]  # flag[i][j] 表示前 i 个物品放入容量为 j 的背包最大价值时装入物品的最大编号

# dp 求解最大价值并更新 flag
for i in range(1, N + 1):
    for j in range(1, W + 1):
        for k in range(min(n[i], j // w[i]) + 1):
            if dp[i][j] < dp[i - 1][j - k * w[i]] + k * v[i]:
                dp[i][j] = dp[i - 1][j - k * w[i]] + k * v[i]
                flag[i][j] = k
ans = dp[N][W]

# 追踪解方案
sol = [0] * N
j = W
for i in range(N, 0, -1):
    sol[i - 1] = flag[i][j]
    j -= flag[i][j] * w[i]
# 输出结果
print(f"最大价值为:{ans}")
print(f"取得最大价值时的物品组合为:{sol}")

结果

最大价值为:31
取得最大价值时的物品组合为:[0, 1, 2, 2]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小嗷犬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值