c++ - 如何在递归中使用记忆来解决0-1背包问题而没有运行时错误以在c ++中进行较大的输入?
Posted
技术标签:
【中文标题】c++ - 如何在递归中使用记忆来解决0-1背包问题而没有运行时错误以在c ++中进行较大的输入?【英文标题】:How to use memorization in recursion to solve 0-1 Knapsack problem without Runtime Error for larger inputs in c ++? 【发布时间】:2021-06-23 18:52:21 【问题描述】:我在 GFG 练习门户中做这个 0-1 背包问题。对于小输入,代码运行良好! .对于更大的输入,我收到“超出时间限制”错误。我是 DP 概念的新手,有人可以解释我的错误是什么。我使用过(递归+记忆)。
问题链接:https://practice.geeksforgeeks.org/problems/0-1-knapsack-problem0945/1#
代码如下:
int a[10005][10005]; // I have declared this globally.
int knapSack(int W, int wt[], int val[], int n)
memset(a,-1,sizeof(a));
if (n == 0 || W == 0)
return 0;
if(a[n][W]!=-1)
return a[n][W];
if (wt[n - 1] > W)
a[n][W]=knapSack(W, wt, val, n - 1);
return a[n][W];
if(wt[n - 1] <= W)
a[n][W]= max(val[n - 1]+ knapSack(W - wt[n - 1],wt, val, n - 1),knapSack(W, wt, val, n - 1));
return a[n][W];
对于较小的输入:
对于输入:
3 // n=no.of 输入
4 //包的W容量
1 2 3 //val[ ]
4 5 1 //重量[ ]
你的输出是: 3
对于较大的输入:
“超过时间限制”
【问题讨论】:
调用memset()
可能是罪魁祸首。应该在knapSack()
之外完成。
请问这些10005
魔法数字是从哪里来的?链接的问题陈述中提到的限制都是1000
。
@Bob__ 是的,您是对的,限制是 1000。出于安全目的,我想占用 '5' 额外空间,即 1000+5=1005 以避免边界冲突。但由于我的数学不好,我把它设为 10005 而不是 1005。但这不是问题,因为额外的空间不是导致程序崩溃的。如果是这种情况,我会收到内存限制错误。
【参考方案1】:
memset(a,-1,sizeof(a));
memset
接受 bytes 中的参数,因此您清除了 1/4 的数组(取决于您的 int 大小)。幸运的是,值 -1 变成了字节 0xff,当它放在 int
的每个字节中时,再次代表 -1。在这方面,值 -1 和 0 是特殊的。通常,最好使用std::fill
。 (虽然这会因为使用二维数组而变得复杂)
好的,你有像这样的递归调用:a[n][W]=knapSack(W, wt, val, n - 1);
并且大概你仍然需要那些你已经计算过的相同的。
您需要保存所有已计算的数据库。一个简单的方法是创建一个std::map
,键是一个包含四个值的结构,结果是值。用一个新函数包装你的真实函数(重命名),该函数首先检查数据库,如果不存在,则调用真实函数,将结果存储在数据库中,然后返回。
啊,您似乎已经通过全局数组实现了记忆化...正如Bob 在评论中指出的那样,memset
是递归函数在内部,因此每次调用都会擦除取出保存的值。
【讨论】:
嗯,不,那是not the problem 与memset
的通话。该数组应该在函数外部初始化。以上是关于c++ - 如何在递归中使用记忆来解决0-1背包问题而没有运行时错误以在c ++中进行较大的输入?的主要内容,如果未能解决你的问题,请参考以下文章