为啥在 0-1 背包问题中我们使用 INT_MIN 以及为啥 0 不起作用
Posted
技术标签:
【中文标题】为啥在 0-1 背包问题中我们使用 INT_MIN 以及为啥 0 不起作用【英文标题】:why in 0-1 Knapsack problem we use INT_MIN and why the 0 is not working为什么在 0-1 背包问题中我们使用 INT_MIN 以及为什么 0 不起作用 【发布时间】:2021-09-03 07:47:48 【问题描述】:为什么在 0-1 背包问题中 INT_min 用于 (max
#include<iostream>
using namespace std;
int solve(int v[], int w[] , int n ,int max)
if (max < 0)
return INT_MIN; // here this is giving me problem if I use 0 in place of
// INT_min I get wrong ansewrs
if (n < 0 || max == 0)
return 0;
int include = 0 ;
int exclude = 0 ;
include = v[n]+solve(v,w, n-1,max - w[n]);
exclude = solve(v,w, n-1,max);
if(include<exclude)
return exclude;
else
return include;
int main()
int v[] = 20, 5, 10, 40, 15, 25 ;
int w[] = 1, 2, 3, 8, 7, 4 ;
int W = 10;
int n = sizeof(v) / sizeof(v[0]);
cout<< solve(v,w,n-1,W)<<endl;
return 0;
对于这个数组,如果我在返回语句中使用 INT_MIN,我得到答案 60,如果我在返回语句中使用 0,我得到 65。
【问题讨论】:
请注意,在这种情况下,编写一个返回 0 而不是 INT_MIN 的背包实现是可能的(实际上更好)。 INT_MIN 的使用在这里有点技巧。 【参考方案1】:假设我们正在手动模拟递归过程。
首先,我们选择最后一个对象 (v=25, w=4
) 并删除最后一个对象 (15v, 7w
)。现在我们有 25 个值和 4 个权重。之后,在下一步N-3
中,N 是对象的数量,我们将确定是否包含最后三分之一的对象(40v, 8w
)。
那么这里是当前步骤中的两个后续递归调用:
include = 40+solve(v,w, N-4,(10 - 4) - 8);
exclude = solve(v,w, N-4,(10-4));
如果在max < 0
时返回INT_MIN
,则include = 40 + INT_MIN = SOME_EXTREMELY_SMALL_VALUE
。和exclude = SOME_NON_ZERO_VALUE > include
。考虑到当前剩余容量仅为10-4=6
,这意味着不能拾取最后三分之一的对象,因为它会溢出背包。
如果改为返回0
,则返回include = 40+0=40
。和exclude = SOME_VALUE < 40
。这意味着实际上除了最后一个对象外,最后一个第三个对象也被拾取,即使它刚刚超出背包,导致总重量为4+8=12>10
和无效答案40+25=65
。
【讨论】:
你的回答太复杂了,能不能用简单的形式给出 正如@Stef 所说,INT_MIN
只是将max > 0
的情况标记为无效的技巧。如果不将这些标记为无效,则将背包装满将被视为可能的解决方案,这会给出错误的答案。
你觉得哪个部分比较复杂?
@Dummy 如果我返回 0,过度填充如何成为有效情况,这无关紧要,应该退出值为 0 的函数实例。
0
在递归中返回给它的父 solve(v, w, N-3, 4)
函数。在solve(v, w, N-3, 4)
内部,solve(v,w, N-4,(10 - 4) - 8)
的计算结果为0
,所以include = 40 + 0 = 40
。 include
将与小于include
的exclude
进行比较。所以这个include
被选中,最终答案是include + PREVIOUSLY_CHOSEN_OBJECTS = 40 + 25 = 65
。以上是关于为啥在 0-1 背包问题中我们使用 INT_MIN 以及为啥 0 不起作用的主要内容,如果未能解决你的问题,请参考以下文章
用回溯法做0-1背包问题,这两行(程序中标注)是干嘛?为啥又要减?