写写代码系列032:整数拆分(动态规划)
Posted 阿尔法放荡不羁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了写写代码系列032:整数拆分(动态规划)相关的知识,希望对你有一定的参考价值。
题目描述
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。返回你可以获得的最大乘积。
示例1:如n = 2,则返回1(2 = 1 + 1,1 * 1 = 1)
示例2:如n = 10,则返回36(10 = 3 + 3 + 4,3 * 3 * 4 = 36)
题目解析
本题是leetcode上343号问题。
解法1:暴力解法
当一个问题没有头绪的时候,我们可以想一想,是否可以用枚举的方法,计算出全部拆分方案,以此来解决问题呢?事实上,对于本问题,回溯遍历将一个数做分割的所有可能性,是可以解决的,时间复杂度是指数级的,为O(2^n)。
我们以分割4为例。第一步,可以将4分割为1+3,2+2,3+1三种方案。
在这样的分割策略下,我们只需要求出分割3获得的最大乘积,再乘以1,就是一个备选答案。同理,我们获得分割2获得的最大乘积,再乘以2,又是一个备选答案。我们获得分割1获得的最大乘积,再乘以3,又是一个备选答案。这三个备选答案中取最大值就解决了分割4最大乘积这个问题。接下来,对于分割3,分割2,分割1获得最大乘积又可以按照同样的策略进行分割:将3分割成1+2,2+1,将2分割成1+1,1不可再分。
现在,对于4的所有分割形式就都遍历出来了,我们也能比较出所有分割方式中最大的乘积是谁。对于更一般的形式,如果我们是分割n获得最大乘积,递归树则如下所示。
事实上,求解分割n所获得的最大乘积这样的一个最优化问题,可以拆分成若干个子问题,通过求子问题的最优解,可以获得原问题的最优解,这样的形式叫做最优子结构。比如,通过求解分割n-1获得的最大乘积,分割n-2获得的最大乘积,……,分割1获得的最大乘积,将这些子问题的答案综合起来,就可以获得分割n获得的最大乘积的答案。事实上,也正是因为最优子结构的存在,我们上述的递归树才得以存在。
解法2:动态规划
基于最优子结构的思路,我们需要有一个数组,这个数组用来储存分割1获得的最大乘积,分割2获得的最大乘积,……,分割n获得的最大乘积这些答案,而我们所需要返回的就是数组中的最后一个元素:分割n获得的最大乘积。事实上,根据最优子结构的思路,分割i获得的最大乘积 = max(j * (i - j),j * 分割i-j的最大乘积),其中1≤j≤i-1,而分割i-j的最大乘积,又可以根据最优子结构的思路继续往前找。
程序实现
class Solution:
def integerBreak(self, n: int) -> int:
# 初始化储存分割i最大结果的数组,memo[i]表示将数字i分割后得到的最大乘积
memo = [-1] * (n + 1)
memo[0] = 1
for i in range(1,n + 1):
# 求解memo[i]
for j in range(1,i):
# 将i分割成j和i-j两部分
# 分割i的最大乘积为:j*(i-j),j*将i-j继续分割的最大乘积
# 根据最优子结构,将i-j继续分割的最大乘积就是memo[i-j]
memo[i] = max(max(memo[i],j * (i - j)),j * memo[i - j])
return memo[n]
扫码关注
以上是关于写写代码系列032:整数拆分(动态规划)的主要内容,如果未能解决你的问题,请参考以下文章