1. 程式人生 > >LeetCode343 整數拆分詳解

LeetCode343 整數拆分詳解

題目詳情

給定一個正整數 n,將其拆分為至少兩個正整數的和,並使這些整數的乘積最大化。 返回你可以獲得的最大乘積。

示例 1:

輸入: 2
輸出: 1
解釋: 2 = 1 + 1, 1 × 1 = 1。

示例 2:

輸入: 10
輸出: 36
解釋: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

說明: 你可以假設 不小於 2 且不大於 58。

題目解析

關鍵是找到狀態方程, 我們設定dp[i]表示整數i的最大乘積, 那麼把問題分成子問題, 我們發現dp[i] 與前面的dp[1] ~ dp[i-1]都有關係, 關係很容易找到

dp[i] = max(j * (i-j), j *
dp[i - j]) j = 1,2 ······ i -1

而初始條件dp[1] = 1, dp[2] = 2, dp[3] = 3

為什麼要加上 j * (i-j)的比較呢, 是為了處理dp[i] < i的情況, 例如 n=4, 那麼如果不考慮 j * (i-j), 因為dp[2] = 1 <2, 所以最終的結果為3, 但是其實把4分成2 * 2, 最終結果為4. 所以要加上 j * (i-j) 直接把i分成兩個數字i, j-i,的情況。

那麼可以由此得到第一種dp程式碼

程式碼一 動態規劃

class Solution {
public:
    int integerBreak
(int n) { vector <int> dp(n +1, 0); int tmp = 0; dp[0] = 0; dp[1] = 1; dp[2] = 1; for (int i = 3; i <= n ; i++) { tmp = 0; for (int j = 1; j <= i - 1; j++) { tmp = max(tmp, max(dp[j] * (i-j), j * (
i - j))); } dp[i] = tmp; } return dp[n]; } };

時間複雜度為0(n^2)

程式碼2 數學推倒

有一些其他的部落格給了更加速度快的演算法, 有一種是利用數學推導, 得出要想使乘積最大, 只需要儘可能的多分成3, 其次是二

數學推導過程 由均值不等式(n個數的算術平均數大於等於它們的幾何平均數):

在這裡插入圖片描述

得:當把輸入的n拆分成幾個相等的數時它們的積最大。

那麼問題來了,拆分成幾個呢?

為了方便使用導數,我們先假設我們可以把n拆分成實數。那麼設每一個數為x,則一共有n/x個數。

設它們的積為f(x),則f(x)=x(n/x),那麼怎麼求f(x)最大值呢?求導數!

f′(x)=(n/x2) * x(n/x) * (1-lnx)

當x=e時取極大值。

而我們題目裡規定x為整數,那麼我們只需要取的x越靠近e越好。那麼2<e<3,而且e=2.71828…,所以取3是最好的,如果取不到3就取2。

冪運算複雜度為O(lgn),所以這個演算法複雜度為O(lgn)。

程式碼如下

class Solution {
public:
    int integerBreak(int n) {
        if(n == 2)
            return 1;
        else if(n == 3)
            return 2;
        else if(n%3 == 0)
            return pow(3, n/3);
        else if(n%3 == 1)
            return 2 * 2 * pow(3, (n - 4) / 3);
        else 
            return 2 * pow(3, n/3);
    }
};