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比较抽象,我们先来思考一个小一点的数,比如先思考 4 4 是如何拆分的。下面画出递归树中的第一次分割:

在这里插入图片描述
要分割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来写了。

原创文章 176 获赞 286 访问量 18万+

猜你喜欢

转载自blog.csdn.net/yjw123456/article/details/105954838