You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

钢条切割DP递归解法正确但加记忆化结果错误,记忆化适用于所有DP吗?

记忆化版本出错的核心原因

你的记忆化状态设计存在根本性问题:
你给helper2设计的参数包含size、index、profit三个可变变量,但缓存用的dp数组只记录了index和size两个维度的状态,完全忽略了profit的影响。你的递归逻辑里,函数返回值是「当前累计profit + 剩余长度可获得的最大新增利润」,这就意味着同一个(index, size)组合,传入的profit不同,最终返回值也完全不同。你缓存了第一次调用该状态的返回值,后续遇到相同(index, size)但profit更高的情况时,直接返回了缓存的错误值,最终结果自然偏小。
以你的测试用例为例,就是某一个(index, size)状态第一次调用时传入的profit比后续正确路径的profit少1,缓存了错误的48相关结果,最终输出比正确值小1的48。

正确的记忆化改造方案

记忆化缓存的状态必须满足「同一状态的返回值固定,和到达路径无关」,所以你需要把累计的profit从参数中移除,改成让递归函数直接返回「剩余长度为size、处理到第index个切割项时,能获得的最大利润」,累计逻辑放到递归返回时计算即可,修改后的代码如下:

// 修正后的递归+记忆化解法
int helper2(vector<int> &price, int size, int index, vector<vector<int>> &dp)
{
    if (size == 0)
        return 0;
    if (size < 0 || index == price.size())
        return INT_MIN;
    if (dp[index][size] != -1)
        return dp[index][size];
    // 选择当前切割长度,还可以继续选当前长度
    int take = INT_MIN;
    if (size >= index + 1) {
        take = price[index] + helper2(price, size - (index + 1), index, dp);
    }
    // 不选当前切割长度,跳到下一个
    int notake = helper2(price, size, index + 1, dp);
    return dp[index][size] = max(take, notake);
}

int cutRod2(vector<int> &price)
{
    int n = price.size();
    vector<vector<int>> dp(n + 1, vector<int>(n + 1, -1));
    return helper2(price, n, 0, dp);
}

修改后运行测试用例就会输出正确的49。

记忆化的适用范围

记忆化不是对所有DP问题都适用,只有满足以下两个核心条件的问题才能用记忆化优化:

  • 存在重叠子问题:同一个子问题会被重复调用多次,缓存才有价值,否则反而会额外消耗存储资源
  • 满足无后效性:某个状态的最优解只和其子状态的最优解有关,和到达该状态的路径无关。你原来的写法就是把路径上的累计利润作为参数传入,破坏了无后效性,才导致缓存失效。
    除此之外,如果问题的状态维度极高,记忆化的空间开销会超出系统限制,这种场景下也不适合使用记忆化,更推荐用递推式DP甚至滚动数组优化的递推写法。

内容的提问来源于stack exchange,提问作者Premnaath Varadharajan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 09:42:01