动态规划之背包问题

本文详细介绍了背包问题中的0-1背包和完全背包问题,包括各自的题解思路和状态转移方程。0-1背包问题中物品不可分割,每个物品有重量和价值,目标是求解在不超过背包容量的情况下,如何选取物品以获得最大价值。完全背包问题则允许物品无限数量,重点在于如何利用动态规划找到恰好装满背包的方法数。文章通过具体例子和伪码解释了解决这些问题的动态规划策略。
摘要由CSDN通过智能技术生成

背包问题

0-1 背包问题

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQFOXKce-1641524249049)(%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98.assets/1.png)]

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

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

算法返回 6,选择前两件物品装进背包,总重量 3 小于 W,可以获得最大价值 6。

题目就是这么简单,一个典型的动态规划问题。这个题目中的物品不可以分割,要么装进包里,要么不装,不能说切成两块装一半。这就是 0-1 背包这个名词的来历。

解决这个问题没有什么排序之类巧妙的方法,只能穷举所有可能,根据我们 动态规划详解 中的套路,直接走流程就行了。

0-1背包问题模板

dp 数组的定义:

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-1 时表示第 i 个物品的价值和重量。

dp[i-1][w - wt[i-1]] 也很好理解:你如果装了第 i 个物品,就要寻求剩余重量 w - wt[i-1] 限制下的最大价值,加上第 i 个物品的价值 val[i-1]

综上就是两种选择,我们都已经分析完毕,也就是写出来了状态转移方程,可以进一步细化代码:

注意为什么i不从0开始

//01背包
for (int i = 1; i <= N; i++) {
   
    for (int w = 1; w <= W; w++) {
   //w可以从nums[i]开始
        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]

完全背包问题

我们可以把这个问题转化为背包问题的描述形式

有一个背包,最大容量为 amount,有一系列物品 coins,每个物品的重量为 coins[i]每个物品的数量无限。请问有多少种方法,能够把背包恰好装满?

与0-1的区别在于每个物品数量无限

题解思路

第一步要明确两点,「状态」和「选择」

状态有两个,就是「背包的容量」和「可选择的物品」,选择就是「装进背包」或者「不装进背包」嘛,背包问题的套路都是这样。

明白了状态和选择,动态规划问题基本上就解决了,只要往这个框架套就完事儿了:

for 状态1 in 状态1的所有取值:
    for 状态2 in 状态2的所有取值:
        for ...
            dp[状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值