LeetCode刷題——343. 整數拆分

題目

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

示例 1:

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

示例 2:

	輸入: 10
	輸出: 36
	解釋: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
說明: 你可以假設 n 不小於 2 且不大於 58。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/integer-break

思路

給定正整數,將其拆分爲至少兩個正整數的和。這個問題能否用遞歸的方法來解呢,簡要的分析可以得出答案是肯定的。

可能直接思考分割n比較抽象,我們先來思考一個小一點的數,比如先思考44是如何拆分的。下面畫出遞歸樹中的第一次分割:

在這裏插入圖片描述
要分割4,可以分割成1+?(這裏的?表示可能爲一個值,也可能是多個值)、2+?和3+?

其中只有1是不可再分的數字。

在這裏插入圖片描述

對於分割4來說,遞歸樹如上。從上可以看出,存在重複子問題。並且分割1可以作爲我們遞歸的終止條件。

如果仔細分析的話,還可以看出,分割1+3和分割3+1是同樣的問題。因此,這裏我們只需要分割到n//2+1即可。

下面畫出分割n的示意圖:
在這裏插入圖片描述
這樣我們就簡要的畫出了分割n的遞歸樹。下面我們先用遞歸的方法來解。

代碼

遞歸

class Solution:
    # 定義遞歸方法
    def breakInteger(self,n):
        if n == 1:
            return 1
        max_result = -1
        for i in range(1, int(n//2) + 1): #從1分割到 n//2 + 1
            max_result = max(i * (n - i), i * self.breakInteger(n - i), max_result)
        return max_result

    def integerBreak(self, n: int) -> int:
        return self.breakInteger(n)

上面我們分析得知,可以通過從1分割到 n//2 + 1來加速算法。

重點來看下這段代碼:
max_result = max(i * (n - i), i * self.breakInteger(n - i), max_result)

max_result = -1初始化爲-1。然後比較當前最大值、直接用i * (n-i)(就是說不對n-i進行分割)、i * breakInteger(n - i)(對n-i進行分割後的最大值)三者之間的最大值,覆蓋到max_result中。

最終返回最大值即可,下面看下提交結果。
在這裏插入圖片描述
果然遞歸的方式會超時。
但是我們不慌,因爲我們會改寫成記憶化搜索了(參閱LeetCode刷題之動態規劃思想)。

記憶化搜索

dp = {}

class Solution:

    def breakInteger(self,n):
        if n == 1:
            return 1
        if n not in dp:
            max_result = -1
            for i in range(1, int(n//2) + 1):
                max_result = max(i * (n - i), i * self.breakInteger(n - i), max_result)
            dp[n] = max_result
        return dp[n]

    def integerBreak(self, n: int) -> int:
        return self.breakInteger(n)

改寫成記憶化搜索也很簡單,保存對傳入的從參數n進行分割的結果即可。

在這裏插入圖片描述

動態規劃

最後改寫成動態規劃的方式。

class Solution:
    def integerBreak(self, n: int) -> int:
        # 因爲n不大於58,我們可以用一個列表來保存,dp[1]=1作爲已知條件,值爲-1表示未計算過
        dp = [1] * 2 + [-1] * (n-1) #dp[0]=1 ,dp[1] = 1, dp[2]到dp[n] = -1
        for i in range(2, n + 1):  # i從2到n
            # 依次計算出dp[i]
            for j in range(1,int(i/2)+1): # j從1到 i/2 + 1
                # 拆分爲j 和 (i-j)
                dp[i] = max(dp[i], j * (i - j), j * dp[i - j]) #因爲i-j < i的,而dp[i]在之前已經計算過了
        return dp[n]

這裏用了兩個循環,依次計算出2到n的分割最大值。

外層循環是2到n;內層循環用1到 i/2 + 1去分割i

在這裏插入圖片描述
這個題目動態規劃的解法還沒有記憶化搜索快。

在這裏插入圖片描述
在Python2中運行時間還是不錯的。

在做完全平方數這個問題,自己寫的解法需要幾秒鐘,不知道哪裏寫錯了,就去看官方解法的時候,看到了這樣一句話。
在這裏插入圖片描述
在LeetCode中提交時間Python3不知道爲啥運行的時間超過Python2。 因此我嘗試用Python2提交了一下,果然如此。

哈,以後就用Python2來寫了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章