LeetCode 343.整數拆分 - JavaScript

題目描述:給定一個正整數 n,將其拆分爲至少兩個正整數的和,並使這些整數的乘積最大化。 返回你能夠得到的最大乘積。javascript

題目分析

題目中「n 至少能夠拆分爲兩個正整數的和」,這個條件說明了 n 是大於 1 的整數。java

對 7 來講,能夠拆成 3+4,最大乘積是 12。git

對 8 來講,能夠拆成 3+3+2,最大乘積是 18。github

解法 1: 動態規劃

狀態數組dp[i]表示:數字 i 拆分爲至少兩個正整數之和的最大乘積。爲了方便計算,dp 的長度是 n + 1,值初始化爲 1。算法

顯然dp[2]等於 1,外層循環從 3 開始遍歷,一直到 n 中止。內層循環 j 從 1 開始遍歷,一直到 i 以前中止,它表明着數字 i 能夠拆分紅 j + (i - j)。但 j * (i - j)不必定是最大乘積,由於i-j不必定大於dp[i - j](數字i-j拆分紅整數之和的最大乘積),這裏要選擇最大的值做爲 dp[i] 的結果。數組

空間複雜度是 \(O(N)\),時間複雜度是 \(O(N^2)\)。代碼實現以下:spa

// ac地址:https://leetcode-cn.com/problems/integer-break/
// 原文地址:https://xxoo521.com/2020-02-15-integer-break/

/**
 * @param {number} n
 * @return {number}
 */
var integerBreak = function(n) {
    const dp = new Array(n + 1).fill(1);

    for (let i = 3; i <= n; ++i) {
        for (let j = 1; j < i; ++j) {
            dp[i] = Math.max(dp[i], j * (i - j), j * dp[i - j]);
        }
    }

    return dp[n];
};

解法 2: 貪心法

力扣上此題給出了提示:多試試幾個例子,找出規律。下面說下我找規律的思路。.net

前面提到:8 拆分爲 3+3+2,此時乘積是最大的。而後就推測出來一個整數,要拆成多個 2 和 3 的和,保證乘積最大。原理很容易理解,由於 2 和 3 能夠合成任何數字,例如5=2+3,可是5 < 2*3;例如6=3+3,可是6<3*3。因此根據貪心算法,就儘可能將原數拆成更多的 3,而後再拆成更多的 2,保證拆出來的整數的乘積結果最大。code

但上面的解法還有不足。若是整數 n 的形式是 3k+1,例如 7。按照上面規則,會拆分紅「3 + 3 + 1」。可是在乘法操做中,1 是沒做用的。此時,應該將 1 和 3 變成 4,也就是「3 + 3 + 1」變成「3 + 4」。此時乘積最大。blog

綜上所述,算法的總體思路是:

  • n 除 3 的結果爲 a,餘數是 b
  • 當 b 爲 0,直接將 a 個 3 相乘
  • 當 b 爲 1,將(a-1)個 3 相乘,再乘以 4
  • 當 b 爲 2,將 a 個 3 相乘,再乘以 2

空間複雜度是 O(1),時間複雜度是 O(1)。代碼實現以下:

// ac地址:https://leetcode-cn.com/problems/integer-break/
// 原文地址:https://xxoo521.com/2020-02-15-integer-break/

/**
 * @param {number} n
 * @return {number}
 */
var integerBreak = function(n) {
    if (n === 2) return 1;
    if (n === 3) return 2;
    // a的含義:n能拆成的3的個數
    const a = Math.floor(n / 3);
    const b = n % 3;

    // n是3的倍數
    if (b === 0) return Math.pow(3, a);
    // n是 3k + 1,例如7。拆成三、三、1。因爲有1對結果沒法有貢獻,因此最後的三、1換成4
    if (b === 1) return Math.pow(3, a - 1) * 4;
    return Math.pow(3, a) * 2;
};

若是想了解詳細的數學推理,請參考《Leetcode 343:整數拆分(最詳細的解法!!!)》

更多資料

相關文章
相關標籤/搜索