1. 程式人生 > 實用技巧 >劍指 Offer 14- I. 剪繩子 + 動態規劃 + 數論

劍指 Offer 14- I. 剪繩子 + 動態規劃 + 數論

劍指 Offer 14- I. 剪繩子

題目連結

還是343. 整數拆分的官方題解寫的更清楚

本題說的將繩子剪成m段,m是大於1的任意一個正整數,也就是必須剪這個繩子,至於剪成幾段,每一段多長,才能使得乘積最大,這就是要求解的問題了

【解題思路1】動態規劃

對於的正整數 n,當 n≥2 時,可以拆分成至少兩個正整數的和。令 k 是拆分出的第一個正整數,則剩下的部分是 n−k,n−k 可以不繼續拆分,或者繼續拆分成至少兩個正整數的和。由於每個正整數對應的最大乘積取決於比它小的正整數對應的最大乘積,因此可以使用動態規劃求解。

  • dp陣列的含義: dp[i] 表示將正整數 i 拆分成至少兩個正整數的和之後,這些正整數的最大乘積。
  • 邊界條件: 0 不是正整數,1 是最小的正整數,0 和 1 都不能拆分,因此 dp[0]=dp[1]=0。
    狀態轉移方程:
    當 i≥2 時,假設對正整數 i 拆分出的第一個正整數是 j(1≤j<i),則有以下兩種方案:
  • 將 i 拆分成 j 和 i−j 的和,且 i−j 不再拆分成多個正整數,此時的乘積是 \(j×(i−j)\)
  • 將 i 拆分成 j 和 i−j 的和,且 i−j 繼續拆分成多個正整數,此時的乘積是 \(j×dp[i−j]\)

因此,當 j 固定時,有 \(dp[i]=max(j×(i−j),j×dp[i−j])\)。由於 j 的取值範圍是 1 到 i−1,需要遍歷所有的 j 得到 dp[i] 的最大值,因此可以得到狀態轉移方程如下:
\(dp[i]= \max_{1≤j<i} {(j×(i−j),j×dp[i−j])}\)


最終得到 dp[n] 的值即為將正整數 n 拆分成至少兩個正整數的和之後,這些正整數的最大乘積。

class Solution {
    public int cuttingRope(int n) {
        int[] dp = new int[n + 1];
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                dp[i]= Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
            }
        }
        return dp[n];
    }
}

【解題思路2】數學:函式極值

直覺上把數拆的越平均他們的積越大。拆分的整數越接近自然引數e,他們的乘積的越大。
數學證明:定義函式 f(x) 表示將給定的正整數 n 拆分成儘可能多的正數 x 的情況下的最大乘積,則可以將 n 分成 \(\frac{n}{x}\) 項,此時 \(f(x)=x^{\frac{n}{x}}\), 通過求導可得f(x)在x=e時取最大值,f(3)>f(2),x=3 時,可以得到最大乘積。

根據 n 除以 3 的餘數進行分類討論:

  • 如果餘數為 0,則將 n 拆分成 m 個 3;
  • 如果餘數為 1,因此將 n 拆分成 m-1 個 3 和 2 個 2;
  • 如果餘數為 2,則將 n 拆分成 m 個 3 和 1 個 2。

上述拆分的適用條件是 n≥4。如果 n≤3,則上述拆分不適用,需要單獨處理

  • 如果 n=2,則唯一的拆分方案是 2=1+1,最大乘積是 1×1=1;
  • 如果 n=3,則拆分方案有 3=1+2=1+1+1,最大乘積對應方案 3=1+2,最大乘積是1×2=2
    這兩種情形可以合併為:當 n≤3 時,最大乘積是 n-1。
class Solution {
    public int cuttingRope(int n) {
        if (n <= 3) {
            return n - 1;
        }
        int quotient = n / 3;
        int remainder = n % 3;
        if (remainder == 0) {
            return (int) Math.pow(3, quotient);
        } else if (remainder == 1) {
            return (int) Math.pow(3, quotient - 1) * 4;
        } else {
            return (int) Math.pow(3, quotient) * 2;
        }
    }
}