343号问题循环内的过程是否不严谨?memo[i] = max3(memo[i], j * (i - j), j * memo[i -j])

来源:9-3 发现重叠子问题 Integer Break

慕田峪2135618

2024-04-26

老师,有一个地方有点疑惑。

在343号问题中,当求memo[i]的值时,自底向上求解,循环语句中memo[i] = max3(memo[i], j * (i - j), j * memo[i - j]),感觉理解起来有点疑惑,j*(i-j)和j*memo[i-j],对应了至少有一个数不分解的情况。但是,更严谨地看,不是应该使用Math.max(j,memo[j])*Math.max(i-j,memo[i-j])来决定memo[i]是否应该更新吗?否则,需要先证明至少采用了一个符合memo[j]<=j的加数j。这可能是对的,但是严谨的证明可能有点绕。

举一个案例,根据题目的例子n=10的最优解是3+3+4=10,3x3x4=36。这时候外层循环i=20,内层循环j=10,计算出来的该次结果Math.max(10*(20-10),10*memo[20-10])=360其实并不正确,或者说循环并没有实现出分解过程包含两个10时的最优解这个意义。实际上一个更优解应该是memo[10]xmemo[20-10]=36x36。

之所以最后全局最优结果正确,是memo[10]*memo[10]在其它遍历的过程中能保证还原出和它一样的分解情况,如memo[3]*memo[17]。这就回到了刚才的一个问题,涉及到需要先证明最优方案中肯定至少采用了一个符合memo[j]<=j的加数j。(虽然可能有人认为这是显然不需要证明的)

主要是循环内的语句,应该严格实现预定的意义。看起来内层循环的意义应该是,在确认j是一个铁定采用原数的方案的最优解。

或者从另一个角度来说,如果memo[i] = max3(memo[i], j * (i - j), j * memo[i - j])的写法不变,假如我们已经求得memo[10]=36>=10了,求10以上的最优解内层循环n应该直接跳过j=10的情况,因为10一定不是最优解中的一个不分解的加数,一定要选memo[10]的分解方案而不选10本身。至于memo[n-10]的情况不影响刚才的分析。

public int integerBreak(int n) {

        if(n < 1)
            throw new IllegalArgumentException("n should be greater than zero");

        int[] memo = new int[n+1];
        memo[1] = 1;
        for(int i = 2 ; i <= n ; i ++)
            // 求解memo[i]
            for(int j = 1 ; j <= i - 1 ; j ++)
                memo[i] = max3(memo[i], j * (i - j), j * memo[i - j]);

        return memo[n];
    }```
写回答

1回答

liuyubobobo

2024-04-27

抱歉,我没有理解你的问题。


在第一段话中,你说的 “需要先证明至少采用了一个符合memo[j]<=j的加数j”我没有理解。

在第二段话中,你觉得例子 n = 10,为什么“这时候外层循环i=20”我没有理解。


我理解你的问题和这个问题是一致的:https://coding.imooc.com/learn/questiondetail/GjNdEXKpjRy69rn4.html


你说:看起来内层循环的意义应该是,在确认j是一个铁定采用原数的方案的最优解。

是这样的。这个假定没有任何问题,因为不管最优解是什么样子,只要能分解成乘法,那么就一定有第一个数字 * 另外一串东西(这串东西可以是一个数字,也可以是一个乘法表达式)。j 在枚举这第一个数字。


继续加油!:)



0
0

玩转算法面试-- Leetcode真题分门别类讲解

课程配套大量BAT面试真题,高频算法题解析,强化训练

7410 学习 · 1150 问题

查看课程