由于深度递归而导致堆栈溢出

Stack overflow due to deep recursion

本文关键字:堆栈 栈溢出 递归 于深度 深度      更新时间:2023-10-16

我有一个任务来解决硬币变化问题,如下所示: 我有 5 种类型的硬币 {1,5,10,25,50},我们想用给定数量的钱对这些硬币进行更改,例如,我可以通过多少种方式形成 11 美分,或者一般的 n 美分?我的程序应该能够处理高达 7489 美分作为输入。

我使用递归和动态编程来解决问题,但在运行时出现堆栈溢出错误(输入>= 3980(

如何解决此错误?

#include <iostream>
using namespace std;
typedef long long ll;
int Cents[5] = { 1,5,10,25,50 }, in, mem[5][7495];
ll CC(int i, int val) { // this is where the recursion happens
if (val == 0)   return 1;
if (i == 5) return 0;
if (mem[i][val] != -1) return mem[i][val];
long long op1 = CC(i + 1, val);
long long op2 = Cents[i] <= val ? CC(i, val - Cents[i]) : 0;
return  mem[i][val] = (op1+op2);
}
int main() { // here i pass an input and fill the mem 2d array with -1's
scanf_s("%d", &in);
memset(mem, -1, sizeof mem);
printf("%lldn", CC(0, in));
system("pause");
return 0;
}

例如,如果输入为 5,则输出应为 2 路 (1(,(5( 如果为 11,则为 4 (1*11(,(1*6,5*1(,(10*1,1*1(,(1*1,5*2( 等等。

您有一个堆栈溢出,因为您的递归深度仅受数组大小和便士数量的限制。 而且您的堆栈状态大小合理。

一种解决方案是使用显式堆栈。

enum coins {
penny, nickle, dime, quarter, half,
coin_count
};
enum {
max_cash = 7495
};
struct coin_problem {
coins lowest_coin = penny;
int money = 0;
};
struct coin_cache {
unsigned long long solution[coin_count][max_cash+1];
coin_cache(){
for(auto& row:solution)
for(auto& val:row)
val=-1;
}
unsigned long long& set(coin_problem p){
return solution[p.lowest_coin][p.money];
}
unsigned long long get(coin_problem p)const{
if(p.lowest_coin==coin_count) return 0;
if(p.money==0) return 0;
return solution[p.lowest_coin][p.money];
}
};
unsigned long long count_solutions( std::vector<coin_problem>& vec, coin_cache& cache){
unsinged long lon retval = 0;
while(!vec.empty()){
auto cur = vec.back();
retval = cache.get[cur];
if (retval != -1) {
vec.pop_back();
continue;
}
++cur.lowest_coin;
auto op1 = cache.get[cur];
if (op1 == -1) vec.push_back(cur);
--cur.lowest_coin;
unsigned long long op2 = -1;
if(Cents[cur.lowest_coin] <= cur.money){
cur.money -= Cents[cur.lowest_coin];
op2 = cache.get[cur];
if (op2==-1) vec.push_back(cur);
cur.money += Cents[cur.lowest_coin];
} else {
op2 = 0;
}
if (op1 != -1 && op2 != -1){
retval = cache.set(cur) = op1+op2;
vec.pop_back();
}
}
return retval;
}
unsigned long long count_solutions( coin_problem p ){
auto pcache = std::make_unique<coin_cache>();
std::vector<coin_problem> todo = {p};
return count_solutions( todo, *pcache );
}

我撕毁了你的递归解决方案并给了它一个手动堆栈。

对于问题堆栈中的每个条目,我们首先查看缓存中是否有解决方案在等待。 如果是这样,我们计算它,并将其存储在缓存中,然后弹出问题。 如果问题堆栈为空,我们将其返回。

如果没有,如果堆栈,我们将首先解决的子问题推到顶部,将当前的问题留待以后解决,并循环直到堆栈为空。

由于我们的手动堆栈存在于免费商店(也称为堆(上,因此我们不太可能在桌面系统上遇到堆栈溢出。

这并不完全有效,因为我们可以在堆栈上为每个问题提供多个条目。 但这最多应该是一个固定因素,并且可以通过重新排序"递归"以首先执行"最深"调用来解决。 或者使问题堆栈成为一组按深度排序的问题。

另一种方法是先解决表中的每个条目,最深的。