343. 整数拆分

343. 整数拆分

题目链接

代码随想录

分析

回溯

这个题可以先分段然后回溯,求出所有的可能性然后求最大值

但是 n 最大是 58,也就是说,可能要递归 58 层,效率可想而知会有多慢。

数学分析 - 贪心

对于这种跟数论有点相关的题目,一定要尝试推演,然后观察

也就是说,只要一个数字是 >=4 的,拆分之后再相乘都比直接相乘得到的结果要大,举个例子:直接乘以 5,没有乘以 2*3=6 的结果大。

所以最大的乘积,一定是全部换成了 2 和 3,我们直接计算 n 可以刚好换成多少个 2 和 3 即可,注意是刚好,不能有余数,如果余数是 1(也只能是 1),与 1 相乘没有增加乘积,就相当于浪费了一位数字。而且应该是尽量多换成 3,举个例子 6,6 可以换成两个 3 和三个 2,但是 3*3=92*2*2=8 要大,所以尽量换成 3 也是一种贪心,这个贪心跟 860. 柠檬水找零 中兑换 20 块钱的时候优先使用 10 块钱是一样的。

如何计算 n 刚好能换成多少个 2 和 3 呢。很简单

效率最高。

动态规划

dp[i] 数组定义为,i 拆分成多个正整数之后,其乘积最大值。

如何求 dp[i] 呢?用 for 循环循环所有一个数乘以另一个数的情况,i 拆分出的两个数为 j 和 i-j,然后比较 j*(i-j)j*dp[i-j] 哪个更大,j*(i-j) 表示只将 i 拆分成两个数,而 j*dp[i-j] 表示将拆分成更多的数,为什么不是拆分成 dp[j]*dp[i-j],其实因为 j 是从 1 到 i-1,所以 j 实际上是已经把所有的情况都考虑在内了,比如 i 等于 10,当前 j 为 6,j 可以拆分成 2 和 4 ,那么当前应该计算 2*4*dp[4] ,但是因为 j 是从 1 开始遍历的,所以实际上我们已经遍历过了 j=2 和 j=4 的情况,j=2 时候,另一半为 dp[8],j=4 时候,另一半为 dp[6],这些乘积的大小我们前面也都比较过,也就是说,j 拆分与 j 不拆分的情况我们都已经对比过了。所以不会有问题。这一点确实有点不好理解。

我想出来的思路只用了一个 for 循环,这个标准方法用了 for 循环嵌套,所以效率会不高。但是上面这个思路确实是将一个问题变成了一个嵌套的子问题。这是解决动态规划问题的基本思路。

一开始我拒绝使用两层 for 循环的思路,其实用两层 for 循环也可以,不是用两层就代表不行。

解题

数论解法

class Solution {

    public int integerBreak(int n) {
        if (n == 2) {
            return 1;
        }
        if (n == 3) {
            return 2;
        }
		// 从 4 开始处理
        int count3 = 0;
        int count2 = 0;
        count3 = n / 3;
        int left = n % 3;
        if (left == 1) {
            count3--;
            count2 = 2;
        } else if (left == 2) {
            count2 = 1;
        }
        int result = 1;
        for (int i = 0; i < count3; i++) {
            result *= 3;
        }
        for (int i = 0; i < count2; i++) {
            result *= 2;
        }
        return result;
    }

}

动态规划

public int integerBreak(int n) {
    int[] dp = new int[n + 1];
	// n从2开始
    dp[2] = 1;
    for (int i = 3; i <= n; i++) {
        for (int j = 1; j <= i - 1; j++) {
	        // 至少要拆分出1
            dp[i] = Math.max(dp[i], Math.max(j * dp[i - j], j * (i - j)));
        }
    }
    return dp[n];
}

相关题