LeetCode343 整數拆分詳解
題目詳情
給定一個正整數 n,將其拆分為至少兩個正整數的和,並使這些整數的乘積最大化。 返回你可以獲得的最大乘積。
示例 1:
輸入: 2 輸出: 1 解釋: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
輸入: 10 輸出: 36 解釋: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
說明: 你可以假設 n 不小於 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);
}
};