343. Integer Break

来源:互联网 发布:毕业论文的数据分析 编辑:程序博客网 时间:2024/06/07 01:45

Given a positive integer n, break it into the sum of at least two positive integers and maximize the product of those integers. Return the maximum product you can get.

For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4).

Note: You may assume that n is not less than 2 and not larger than 58.

这一题是给一个数字n,将n拆分为k(k >= 2)个数字, 使得n = x1 + x2 + ... + xk

然后计算x1 * x2 * ... * xk,使得积最大


我们来找递推关系

设f(n)为最佳的拆分选择,那么f(n) = max{f(n-x) * x}, 0<x<n

那么我们的动态规划程序可以如下:

int integerBreak(int n) {
       int save[59];      //保存数据

        save[1] = 1;
save[2] = 1;     //第一个数
for (int i = 3; i <= n; i++){
save[i] = 0;
for (int j = 1; j < i; j++)   
save[i] = max(save[i], (i-j) * save[j]);
}  
return save[n];
}

这个代码看似正确,实际上其中含着一个致命的bug

我们容易知道,save[2] = 1 < 2, save[3] = 2 < 3,这两个数的拆分后的积是小于自己的

那么,利用到这两个值的数就会出错。例如,我们容易知道 5 = 2 + 3, save[5] = 2 * 3 = 6是最佳选择。如果按照上面代码来计算的话,就会出现

5 = 2 + 3, save[5] = 2 * save[3] = 2 * 2 = 4,结果显然是错误的。

如何解决这个问题呢?有一个办法。我们令 save[2] = 2, save[3] = 3 就好。

修改后代码如下:

int integerBreak(int n) {
        int save[59];
if (n <= 3) return n-1;  //n小于3直接返回
save[1] = 1;      //辅助用
save[2] = 2;
save[3] = 3;
for (int i = 4; i <= n; i++){
save[i] = 0;
for (int j = 1; j < i; j++)
save[i] = max(save[i], (i-j) * save[j]);
}  

return save[n];
}

当然这样做之后结果是正确的,但是效率较低,时间复杂度为O(n^2),没有更好的办法呢?

我们通过观察答案了解到,实际上所有的数,经过最佳拆分之后,拆分出的数要么是2,要么是3,要么是4

就是说,所有数的最佳拆分,都是由2、3、4组成的

那么我们第二个迭代就可以修改

for (int j = 2; j <= 4 && j < i; j++)

    save[i] = max(save[i], j * save[i - j]);

如此一来,时间复杂度就变为O(n)

原创粉丝点击