1. 程式人生 > >leetcode343. Integer Break

leetcode343. Integer Break

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.

題目大意是說給定一個正整數n,將其拆分成正好等於至少2個正整數相加,使得這幾個正整數的乘積最大。

Example 1:

Input: 2
Output: 1
Explanation: 2 = 1 + 1, 1 × 1 = 1.

Example 2:

Input: 10
Output: 36
Explanation: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36.

分析:

上面的兩個輸入例子還是比較好懂的,這裡我們詳細進行分析。試幾個輸入用例,我們會發現,如果可以不分解出1,那麼就儘量不分解出1,什麼意思呢,比如n=3,那麼最大乘積的分解方法是1*2=2,但是不會分解成1*1*1=1,因為乘以1對乘積結果沒有影響,還會佔用求和,因此,使用帶1進行分解是比較“虧”的。

第二個問題是,我們既然已經知道了儘量不要用1進行分解,那麼,n滿足什麼條件可以不用分解呢?

n=2,分解成1*1=1;n=3,分解成1*2=2;n=4,分解成2*2=4。也就是說,當n>=4的時候,分解中就不會帶有1。

由此,我們想到一種動態規劃演算法,宣告一個長度為n的陣列,dp[i]=w表示使用題目中所描述的分解方法進行分解得到的最大乘積是w。接下來也就是最重要的部分,如何設定初始值以及尋找到狀態轉移方程。

首先考慮狀態轉移方程(其實一般的動態規劃應該首先考慮初始值,但是這道題比較特殊,因為初始值的設定比較難,而如果從狀態轉移方程入手就知道應該如何設定初始值)

從dp陣列的含義,不難更新dp陣列,下面,以一個具體的例子說明如何更新dp陣列

n=8,想求出最大分解後的乘積,第一種情況是第一個數是1,然後看剩下的7分解後最大乘積是多少,然後將結果相乘,也就是dp[1]*dp[7];

第二種情況是第一個數是2,然後看剩下的6分解後最大乘積是多少,然後將結果相乘,也就是dp[2]*dp[6];在這裡千萬不要以為我是把n=8只拆分成兩個數,2和6,因為6是否分解取決於dp[6]的決策,從這裡就可以看到這是一個典型的由遞迴轉換成的動態規劃(因為當前的決策依賴於之前的決策)。

如果看清了這一點,那麼dp[8]=max(dp[i]*dp[8-i]),其中i取值1-7

好,抽象出來,對於任意位置i,dp[i]=max(dp[j]*dp[i-j])。由此,我們終於找到了狀態轉移方程。

最後一個問題,初始值如何設定?

從dp陣列的含義我們知道,dp[5]表示將5分解之後得到的最大乘積,應該是2*3,也就是dp[2]*dp[3],在這裡,dp[2]=2。由以上分析知道當n<=3的時候,dp[i]=i。分析到這一層,就不難寫出如下的程式碼了。

    int integerBreak(int n) {
        if (n<=3)
            return n-1;
        vector<int>dp;
        dp.push_back(0);
        dp.push_back(1);
        dp.push_back(2);
        dp.push_back(3);
        int i,j, tempMax = 0;
        for (i = 4; i <= n; i++){
            tempMax = i - 1;
            for (j = 2; j <= i/2;j++)
            if (dp[j] * dp[i - j] > tempMax)
                tempMax = dp[j] * dp[i - j];
            dp.push_back(tempMax);
        }
        return dp.back();
    }