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 ++中进行较大的输入?的主要内容,如果未能解决你的问题,请参考以下文章

常见编程模式之动态规划:0-1背包问题

我的背包递归解决方案可以改进吗?

0-1背包问题

动态规划:0/1 背包 - 将组合检索为数组

C++经典算法问题:背包问题(迭代+递归算法)!含源码示例

C++ 算法主题系列之集结0-1背包问题的所有求解方案