其实01背包问题的解决
原文:Jiao机器感知8月7日的问题并不复杂,但是又很多的朋友都不太了解,因此呢,今天小编就来为大家分享01背包问题的解决
原文:Jiao机器感知8月7日的一些知识,希望可以帮助到大家,下面我们一起来看看这个问题的分析吧!
我们有$N$ 件物品,物品$i$ 的重量为$w[i]$,价格为$p[i]$。我们假设所有物品的重量和价格都是非负的,背包能承受的最大重量W为。如果限制每项只能选择0或1,则该问题称为0-1背包问题。
2. 二维动态规划解法
二维动态规划实际上就是填一张表。表中的数值代表可以获得的最大总值。表格如下:
现在我们分析动态规划中的状态转换。假设当前要寻找的状态为table[i][j],其中$i$表示第$i$个物品,$j$表示当前背包容量。由于是01背包问题,即某一项只能选择或不选择,即目前有两种可能的最优解,即选择或不选择,对应的总值为:table[ i-1][j],表[i-1][j-w[i]]+v[i],解释如下:
当不选择第$i$项时,当前最大值与状态为$i-1$、容量为$j$时的最优解相同;当选择第$i$项时,当前最大值为状态$i-1$,但容量为$j-w[i]$,当其值为$v[i]时加上当前项的值$,可见该问题的状态转移方程如下:
表[i][j]=max(表[i-1][j],表[i-1][j-w[i]]+v[i])
有了这个状态转移方程,我们就可以编写程序了。示例程序如下:
//总重量
#定义W (18)
//总的东西
#定义N (7)
int表[N+1][W+1]={0};
int 值[N+1]={0, 12, 10, 8, 11, 14, 7, 9};
int 权重[N+1]={0, 4, 6, 5, 7, 3, 1, 6};
无效背包()
{
整数k,w;
对于(k=1;k=N;k++){
对于(w=1;w=W;w++){
if (权重[k] w) {
表[k][w]=表[k-1][w];
} 别的{
int value1=表[k-1][w-权重[k]] + value[k];
int value2=表[k-1][w];
如果(值1值2){
表[k][w]=值1;
} 别的{
表[k][w]=值2;
}
}
}
}
}
运行结果如下。对比网络图,我们可以看到结果是完全一样的:
3. 最优解回溯
通过上述方法可以找到背包问题的最优解,但是我们仍然不知道最优解由哪些商品组成。因此,我们需要根据最优解进行回溯,找出解的组成。根据填表时的规则,通过反推法可知:
一个。如果table[i][j]==table[i-1][j],说明第$i$项没有被选中,那么上一步就是table[i-1][的位置j] 地点;
b.如果table[i][j] !=table[i-1][j],则表示选择了第$i$个产品,然后根据table[i][j]=table[i-1] [j-w [i]] + v[i] 可以得到之前的位置为table[i-1][j-w[i]];
c.重复此操作,直到找到i=0。
示例代码如下:
无效show_track()
{
向量对int,int轨道;
int i=N,j=W;
而(i0){
if (表[i][j]==表[i-1][j]) {
我- ;
} 别的{
track.push_back(make_pair(i, j));
int w=权重[i];
j-=w;
我- ;
}
}
for (int i=0; itrack.size(); i++) {
cout track[i].first ‘ ‘;
}
}
4. 复杂度分析
根据程序分析,我们需要填写的表数量为$N*W$,所以时间复杂度和空间复杂度都是$O(N*W)$,因为必须遍历所有可能性才能得到最优解决方案,不像有序数组可以分成两部分什么的,所以时间复杂度无法优化,但是空间复杂度还是可以优化的,因为我们每次更新都只和前一时刻的状态有关,所以有不需要通过保留这么多时刻的状态,空间复杂度可以优化到$O(W)$。首先分析一下上一步的核心代码:
对于(w=1;w=W;w++){
对于(k=1;k=N;k++){
if (权重[k] w) {
表[k][w]=表[k-1][w];
} 别的{
int value1=表[k-1][w-权重[k]] + value[k];
int value2=表[k-1][w];
.
}
可以看到,在核心代码部分,每次更新都只与前一时刻的状态相关。对应于表,每次更新只与表中的一行数据相关。这就是为什么空间复杂度可以优化为$O(W)$的原因,如果简单的优化成下面的代码:
对于(w=1;w=W;w++){
对于(k=1;k=N;k++){
if (权重[k] w) {
表[w]=表[w];
} 别的{
//需要w前面的w-weight[k]处的值
int value1=表[w-权重[k] + 值[k];
int value2=表[w];
表[w]=.
}
上面的代码不会给出正确的结果,为什么?我们从左到右扫描行,需要取$w-weight[k]$,也就是$w$左边的值,但是我们从左边开始更新,那么我们需要的值就是初始更新被覆盖,得到的值是当前时刻的值,而不是上一时刻的期望值。显然,不可能得到正确的结果。为了避免这种覆盖现象,我们可以通过反向扫描的方式进行更新。即从右向左更新,这样更新的位置就不会被左边所需要,也能得到正确的结果。代码如下:
无效背包优化()
{
//只使用表中的第一行
整数k,w;
对于(k=1;k=N;k++){
对于(w=W;w=1;w–){
if (权重[k] w) {
表[0][w]=表[0][w];
} 别的{
int value1=table[0][w-weight[k]] + value[k];
int value2=表[0][w];
如果(值1值2){
表[0][w]=值1;
} 别的{
表[0][w]=值2;
}
}
}
}
}
5. 总结
01 背包问题的求解采用二维动态规划。简单二维动态规划的时间复杂度和空间复杂度都是$O(N*W)$。分析代码可以看出,状态转移时,当前状态只与前一时刻的状态相关,因此空间复杂度可以优化为$O(W)$。优化代码时需要注意的是:行更新顺序需要改变,以避免新的状态覆盖前一个状态。需要从右向左更新当前的状态;得到最优解后,需要回溯最优解。该方法步骤可以如上所述进行。
附录
原创文章,作者:小su,如若转载,请注明出处:https://www.sudun.com/ask/98496.html
用户评论
在哪跌倒こ就在哪躺下
这个解释很清楚!我之前一直搞不懂01背包问题的解法,看完你的文章豁然开朗了!动态规划真是太牛逼了
有10位网友表示赞同!
十言i
作为一名计算机专业的学习者,对算法问题非常感兴趣。这篇关于01背包问题的文章讲解得比较全面,尤其是你提供的代码示例真的很 hilfreich !
有8位网友表示赞同!
别悲哀
这个解法确实有效,但是运行时间有点慢啊,有没有更快的解法?我这里需要处理的数据量比较大…
有15位网友表示赞同!
纯真ブ已不复存在
对于初学者来说可能有点难理解吧,算法模型的介绍可以用更通俗易懂的语言解释一下好吗?
有5位网友表示赞同!
軨倾词
文章写的不错,但为什么没有提到贪心算法这种解法呢?我觉得贪心算法在某些特殊情况下的效率更高。
有18位网友表示赞同!
凉城°
终于找到了解决01背包问题的思路!我已经开始尝试用代码实现它了,期待能够成功运行!感谢你的分享!
有9位网友表示赞同!
棃海
动态规划的思想的确很巧妙,但对于复杂程度更高的背包问题是否还有其他更优解法的存在呢?
有12位网友表示赞同!
浮光浅夏ζ
这个01背包问题的解法是我刚学到的算法之一,感觉有点抽象难以理解。请问是否有更加直观的方法来解释它?
有20位网友表示赞同!
安之若素
我很想尝试不同的解决方法,例如遗传算法或神经网络算法来求解01背包问题,不知道这有没有什么资源可以参考?
有7位网友表示赞同!
墨城烟柳
感觉代码实现太繁琐了,是不是有更简洁的解题思路呢?像一些基于图形化或者可视化的方法或许更容易理解?
有5位网友表示赞同!
高冷低能儿
写的很棒啊!我已经开始应用到我的个人项目中去了,你的解法有效率很高。感谢您的贡献!
有5位网友表示赞同!
恰十年
对于01背包问题来说,最优解应该是以最大价值为目标的吧?其他的目标函数如何定义呢?
有13位网友表示赞同!
浅巷°
感觉你的代码写得很专业啊!请问你是在哪种语言下编写的呢?是否可以分享更多关于编程方面的知识呢?
有19位网友表示赞同!
花花世界总是那么虚伪﹌
文章内容不错,但对于01背包问题的不同变体,比如带约束的背包问题,有没有什么更具体的解决方案介绍呢?
有13位网友表示赞同!
浮殇年华
我个人觉得动态规划这种解法还是比较耗时的,有没有一些其他的高效算法可以参考?例如使用树搜索或者分支限界法?
有10位网友表示赞同!
■□丶一切都无所谓
01背包问题是一个经典的组合优化问题,这篇博文对它的讲解很有针对性!学习到了很多新的知识点。
有20位网友表示赞同!
一纸愁肠。
感谢分享!这篇文章不仅清晰地描述了01背包问题的解法,还提供了具体的代码示例,非常实用!
有20位网友表示赞同!
短发
我还在尝试理解动态规划算法的原理,这篇博文对我的帮助很大,希望下次可以分享更多关于其他算法的讲解。
有6位网友表示赞同!