背包问题(0-1背包和完全背包)基于python实现

一. 前言

背包问题是一个很经典的问题,包括八个不同类别,但实际面试中,一般知道0-1背包和完全背包就可以应付面试了,本文将从两个基本背包问题进行讨论和实现。
背包问题:
有一个承重为W的背包和N个物品,每个物品重量分别为wt[i],每个物品价值为v[i],求问怎么装背包才能使得包内物品价值最高?注意:包内物品重量总和不能超过W

二. 0-1背包

求最值问题首先想到动态规划来做,背包问题就可以采用dp来做

  1. 首先弄清楚dp的状态和选择
    状态:背包可以承重的重量W和可选择的物品,因此需要采用二维数组dp
    选择:将物品装入或不装入背包
  2. 其次弄清楚dp的含义
    dp[i][w]:表示前i个物品在背包承重为w时背包内的最大价值
  3. 接着写出dp状态转移方程
for i in range(1,N+1):
	for w in range(1,W+1):
		if w-wt[i-1]<0:
			dp[i][w] = dp[i-1][w]
		else:
			dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - wt[i - 1]] + vt[i - 1])
  1. 最后初始化dp数组和考虑边界条件
    dp = [[0] * (W + 1) for _ in range(N + 1)]
    边界条件:当包内不装任何物品的时候,价值为0,即dp[…][0]=0

下面代码值得注意的循环是从1开始的,而物品重量和价值列表是从0开始的,因此i-1表示第i个物品
完成的代码如下:

"""
动态规划
N=3个物品,每个物品重量wi=[2,1,3],价值vi=[4,2,3]
背包最大的容纳重量为W=4
"""
def dp_bagpack():
    N = 3
    W = 4
    wt = [2, 1, 3]
    vt = [4, 2, 3]
    dp = [[0] * (W + 1) for _ in range(N + 1)]
    for i in range(1, N + 1):
        for w in range(1, W + 1):
            if w - wt[i - 1] < 0:  # w < wt[i - 1],当前背包容不下下一个物品的重量,此时选择不放入
                dp[i][w] = dp[i - 1][w]
            else:  # 背包可以容纳下一个物品,i-1和w-1表示当前物品i,因为for循环是i=1开始的
                dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - wt[i - 1]] + vt[i - 1])
    return dp[N][W]

还可以进一步优化,使用一维数组dp

"""
动态规划
N=3个物品,每个物品重量wi=[2,1,3],价值vi=[4,2,3]
背包最大的容纳重量为W=4
"""
def dp_bagpack2():
    N = 3
    W = 4
    wt = [2, 1, 3]
    vt = [4, 2, 3]
    dp = [0] * (W + 1)  # dp[j]代表的是当包重量达到j的时候,此时包内物品的最大值
    dp[0] = vt[0]
    for i in range(1, N):
        for w in range(W, -1, -1):  # 要逆序遍历
            if w - wt[i - 1] >= 0:  # 如果包内重量还没装满,则判断取最大值
                dp[w] = max(dp[w], dp[w - wt[i - 1]] + vt[i - 1])
    return dp[W]

两个程序运算结果都为6

三.完全背包

完全背包和0-1背包唯一不同的地方就是在完全背包问题中物品是无限可取的,但不能超过背包承重量W;而0-1背包则是物品只能取一次,不能重复去,也不能超过背包承重量W
完全背包求解思路和0-1背包几乎一致,只是在代码个别地方需要注意一下

"""
动态规划
N=3个物品,每个物品重量wi=[1, 2, 3],价值vi=[2, 4, 3]
背包最大的容纳重量为W=4
物品可重复取
"""
def dp_bagpack():
    N = 3
    W = 4
    wt = [1, 2, 3]
    vt = [2, 4, 3]
    dp = [[0] * (W + 1) for _ in range(N + 1)]

    for i in range(1, N + 1):
        for w in range(1, W + 1):
            if w - wt[i - 1] < 0:
                dp[i][w] = dp[i - 1][w]
            else:
                # 与01背包不同的就是dp[i][w-wt[i-1]]+vt[i-1]与dp[i-1][w-wt[i-1]]+vt[i-1]
                dp[i][w] = max(dp[i - 1][w], dp[i][w - wt[i - 1]] + vt[i - 1])
    return dp[N][W]

优化版

"""
动态规划
N=3个物品,每个物品重量wi=[1, 2, 3],价值vi=[2, 4, 3]
背包最大的容纳重量为W=4
物品可重复取
"""

# 优化版
def dp_bagpack2():
    N = 3
    W = 4
    wt = [2, 1, 3]
    vt = [4, 2, 3]
    dp = [0] * (W + 1)
    for i in range(0, N):  # 此时不是从1开始,而是从0开始,因为可重复取
        for w in range(0, W + 1):  # 记住:要正序取,从0开始
            if w - wt[i] >= 0:  # 只需要判断包未装满的情况
                # dp[w - wt[i]] + vt[i]表示取物品i,则当前重量w需要减去物品的重量,并加上该物品的价值
                dp[w] = max(dp[w], dp[w - wt[i]] + vt[i])  
    return dp[W]

三. 总结

背包问题,至此掌握

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
背包问题和0-1背包问题都是动态规划问题,它们的主要区别在于背包问题中的物品可以取任意数量,而0-1背包问题中的物品只能取0或1。 以下是背包问题和0-1背包问题Python代码实现: **背包问题** ```python def knapsack(items, capacity): n = len(items) dp = [ * (capacity + 1) for _ in range(n + 1)] for i in range(1, n + 1): weight, value = items[i - 1] for j in range(capacity, -1, -1): if j >= weight: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j] return dp[n][capacity] ``` 在这个代码中,items是一个列表,包含一系列物品及其重量和价值。capacity是背包的容量。函数返回的是在给定容量下可以获得的最大价值。 **0-1背包问题** ```python def knapsack_0_1(items, capacity): n = len(items) dp = [ * (capacity + 1) for _ in range(n + 1)] for i in range(1, n + 1): weight, value = items[i - 1] for j in range(capacity, -1, -1): if j >= weight: dp[i][j] = max(dp[i - 1][j - weight]) if dp[i - 1][j - weight] else 0 else: dp[i][j] = dp[i][j - weight] if dp[i][j - weight] else float('inf') # 若没有选择的选项,取无穷大(即为取值为零)作为代表。 dp[i][j] = min(dp[i][j], dp[i][j-weight]) if dp[i][j] > capacity else dp[i][j] # 最后如果总的容量大于给定的容量,就不能选之前的选项,那么最后的方案应等于剩余容量的情况下的最优方案。 return dp[n][capacity] # 这个点对应的是选择的最后一个物品时能获得的最大价值。因为是从后向前计算,最后一个物品的价值必定小于其单独的值。如果该物品值过大(超出总容量),就考虑替换之前的选项(且最优的替换方式)而不是完全装入背包中。最后,选取一个方案的最大值就是我们的结果。 ``` 这段代码中的物品同样需要给出其重量和价值,返回的是在给定容量下可以获得的最大价值,但是只能选择0或1,不能选择多个物品。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值