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 在枚举这第一个数字。
继续加油!:)
00
相似问题