【问题标题】:How does memoization help here?记忆化在这里有什么帮助?
【发布时间】:2022-01-06 20:04:42
【问题描述】:

我刚刚解决了子集和问题:

给定一个大小为N 的整数数组nums。还给你一个整数B,你需要找出nums中是否存在一个子集,其和为B。如果存在子集,则返回1,否则返回0

约束是:1 <= N <= 1001 <= nums[i] <= 100; 1 <= B <= 10^5;

我解决这个问题的方法如下(0/1背包):

vector<int> n;
int t;
unordered_map<string, long long> m;

int helper(int i, int sum) {
    if(i>=n.size()) return sum==t;
    string str=to_string(i)+"-"+to_string(sum);
    if(m.count(str)) return m[str];

    int val=helper(i+1, sum+n[i]);
    val=max(val, helper(i+1, sum));

    return m[str]=val;
}

int Solution::solve(vector<int> &nums, int B) {
    n=nums;
    t=B;
    m.clear();
    
    return helper(0,0);
}

这得到“接受”。但是,请注意nums 中的所有值都是正数;所以IMO总和只会保持不变/继续增加。 i 也在继续增加。因此,我们永远不会遇到以前存储在记忆表中的值。

但是,如果我删除记忆,它会导致一些大型测试用例的错误答案。我错过了什么?任何递归调用都会遇到以前的状态吗?

【问题讨论】:

  • 这样的问题,你不能提供导致失败的输入,可能是题外话:meta.stackoverflow.com/questions/270585/…。为了创建minimal reproducible example 并提出可接受的问题,您可能需要做更多的工作来开发自己的测试用例。
  • @NateEldredge,谢谢。我只是希望在我的理解中发现我们从未遇到过以前计算的值的逻辑缺陷。
  • 不是很相关,但是string str=to_string(i)+"-"+to_string(sum);:你知道std::pair吗?
  • int val=helper(i+1, sum+n[i]); val=max(val, helper(i+1, sum)); 第二次调用得到的sum 值比第一次要小,所以sum 肯定不是单调递增的。
  • 如果您的数组是2,4,1,5, ...,并且您在跳过 1,5 的同时选择了 2,4,那么您有一个 i=4 和 sum=6 的子问题。另一方面,如果你跳过 2,4 并选择 1,5,那么子问题是 i=4 和 sum=6。

标签: c++ algorithm recursion dynamic-programming memoization


【解决方案1】:

您调用了两次helper,第二次调用的sum 比第一次低。因此,稍后对helper 的调用确实可能与之前的调用具有相同的sum

@user3386109 已经给出了一组具体的num 来证明这一点。至于多长时间,考虑nums = [1, 1, ..., 1] 100 次的情况。然后没有记忆,你会打电话给helper(100, 50)100 choose 50 = 100,891,344,545,564,193,334,812,497,256次。超过 100 个 octillion 调用..需要一段时间。

【讨论】:

    猜你喜欢
    • 2011-07-24
    • 2015-07-30
    • 2011-04-30
    • 2018-06-03
    • 2016-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多