Leetcode 343:整數拆分(最詳細的解法!!!)
阿新 • • 發佈:2018-12-11
給定一個正整數 n,將其拆分為至少兩個正整數的和,並使這些整數的乘積最大化。 返回你可以獲得的最大乘積。
示例 1:
輸入: 2
輸出: 1
解釋: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
輸入: 10
輸出: 36
解釋: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
說明: 你可以假設 n 不小於 2 且不大於 58。
解題思路
這個問題很簡單,可以通過遞迴解決。舉個列子,對於正整數4,我們要知道將4拆分為哪幾個正整數的和,並使這些整數的乘積最大化,那我們只要知道3、2、1的乘積最大化分別是多少,然後從中選出最大值即可,以此類推下去即可。
class Solution :
def integerBreak(self, n):
"""
:type n: int
:rtype: int
"""
if n == 1:
return 1
result = -1
for i in range(1, n):
result = max(result, i*(n - i), i * self.integerBreak(n - i))
return result
很多人在寫上面這個程式碼的時候會將
max(result, i*(n - i), i * self.integerBreak(n - i)) ==>
max(result, i * self.integerBreak(n - i))
寫成下面的那種形式,關鍵問題在於沒有理解函式的定義。integerBreak
是將一個正整數拆分為至少兩個正整數的和,也就是說i * self.integerBreak(n - i)
至少是三個整數的積,那麼我們在比較最大值的時候自然要將i*(n - i)
給加上啦。
上面這種演算法中存在著大量的重複運算(在哪裡呢?)。我們可以通過記憶化搜尋的方式來優化上面的問題。
class Solution :
def integerBreak(self, n):
"""
:type n: int
:rtype: int
"""
if n == 1:
return 1
mem = [-1 for i in range(n + 1)]
mem[1] = 1
for i in range(2, n + 1):
for j in range(1, i):
mem[i] = max(mem[i], j*(i - j), j*mem[i - j])
return mem[n]
實際上這個問題通過數學方法很快就可以解決。我們將一個整數拆分,往往這個拆分後的數中包含整數的話,那麼這些數的乘積最大
2^3 < 3^2
4^3 < 3^4 > 3^3^1
5^3 < 3^5 < 3^3^2
6^3 < 3^6 < 3^3^3
通過上面的例子你會發現這樣的規律:想要乘積最大,那麼一定要將3
作為基底,並且<=4
的餘數不再進行拆分。為什麼會這樣呢?
我們將n
拆分成個數值為x
的整數,那麼這些整數的乘積就是,我們現在的目標就是計算。我們對這個函式求導,得到
當x=e
的時候取最大值。所以我們這裡可以取的整數就是2
和3
,但是
6 = 2 + 2 + 2 = 3 + 3 but
2*2*2 < 3*3
所以就有了前面的結論。但是這裡還有一個問題,就是為什麼要取相同數的乘積,不同數不行嗎?確實不行(~o ̄3 ̄)~
基於上述的論點,我們可以寫出這樣的程式碼,非常的簡潔。
class Solution:
def integerBreak(self, n):
"""
:type n: int
:rtype: int
"""
if n <= 3:
return n - 1
result = 1
while n > 4:
n -= 3
result *= 3
return n * result
如有問題,希望大家指出!!!