经典动态规划0-1背包问题-python3实现

题目描述

给你一个可装载重量为W的背包和N个物品,每个物品有重量和价值两个属性。其中第i个物品的重量为wt[i],价值为val[i],现在让你用这个背包装物品,最多能装的价值是多少?

举个简单的例子,输入如下:

 N = 3, W = 4
 wt = [2, 1, 3]
 val = [4, 2, 3]

输出为6。

第一步

第一步需要明确两点,[状态][选择]
状态,是指如何才能描述一个问题局面。只要给定几个可选物品和一个背包的容量限制,就形成了一个背包问题。因此状态有两个,就是背包的容量可选择的物品
选择也很简单,对于每件物品,你能选择什么?选择就是装进背包或者不装进背包
明白了状态和选择,则动态规划问题基本上就解决了。

for 状态1 in 状态1的所有取值:
	for 状态2 in 状态2的所有取值:
		for ...
		dp[状态1][状态2][...]=择优(选择1,选择2...

第二步 明确dp数组含义

dp[i][w]的定义如下:对于前i个物品,当前的背包容量为w,这种情况下可以装的最大价值是dp[i][w]
根据这个定义,我们想求的答案就是dp[N][W]

边界条件

base case就是dp[0][...]=dp[...][0]=0,因为没有物品或者背包没有空间时,能装的最大价值就是0。

第三步 思考状态转移的逻辑

简单来说,把物品i装进背包不把物品i装进背包如何用代码体现出来。
dp[i][w]表示:对于前i个物品,当前背包的容量为w时,这种情况下可以装下的最大价值是dp[i][w]

如果你没有把这第i个物品装入背包,那么很显然,最大价值dp[i][w]应该等于dp[i-1][w]。你不装嘛,那就继承之前的结果。

如果你把这第i个物品装入了背包,那么dp[i][w]应该等于dp[i-1][w-wt[i-1]] + val[i-1]
这里要注意的是,i是从1开始取的,因此对于valwt,第i个物品的重量或价值的取值应该是i-1
dp[i-1][w-wt[i-1]]也很好理解:你如果想装第i个物品,你怎么计算这时候的最大价值?换句话说,在装第i个物品的前提下,背包能装的最大价值是多少?

显然,你应该寻求剩余重量w-wt[i-1]限制下能装的最大价值,加上第i个物品的价值val[i-1],这就是装第i个物品的前提下,背包可以装的最大价值。

最后代码

def knapsack(W, N, wt, val):
    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:
                dp[i][w] = max(dp[i - 1][w - wt[i - 1]] + val[i - 1], dp[i - 1][w])
    return dp[N][W]
print(knapsack(4, 3, [2, 1, 3], [4, 2, 3]))
#output:
6

注意

每次看到边界条件时,我其实就会非常的不爽。因为这样最后导致了用wt[i-1],val[i-1]来表示第i个物品的价值和重量。我在想,能否不要这个边界条件呢。
我在尝试去编程的时候发现不可以。原因在于,动态规划其实最好有初始状态值,再一步一步进行迭代是最容易的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值