leetcode(力扣) 343. 整数拆分 (动态规划 & 数学方法)

题目描述

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。
返回 你可以获得的最大乘积 。

示例 1:
输入: n = 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。

示例 2:
输入: n = 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

思路分析

先来个例子说明为啥用动态规划,比如所给数值为n=10。
则可以拆分成 2 + 8 = 10,2无法拆分,8可以继续拆。实际上可以发现,这里继续拆的话就是在找8拆分之后的乘积最大值了。也就是说,后一步依赖于前面的最优解。所以想到用动态规划,动态规划就是这样,这一步依赖于前几步的最优解,然后用那个状态转移方程去一步一步推到结果。

老规矩 dp五步走:

1.确定dp含义:
dp[i] 表示 整数i 的最大乘积。

2.确定递推公式:

局部的去想这个问题,一个数 i 的拆分情况。
i 被拆分成 j 和 i-j:
i-j 可能都无法继续拆分,那么此时乘积就是 j * (i-j)。
i-j 可以继续拆分,那么实际上这里就可以直接使用之前计算过的j的最优解 也就是 dp[i-j],此时乘积就是 j *dp[i-j]。

所以当前数值i的最大值就是上面两种情况的一种,在加上其本身,一共三种。
dp[i] = max(dp[i], (i - j) * j, dp[i - j] * j);

3.初始化dp数组:

n= 0 和 n=1的时候都是没法拆的,这俩都定义成0就可以了。

4.确定遍历顺序:

显然 i 是整个遍历的主指针,从 2开始遍历到我们要求的n。
然后就是要拆分的那个j,j从1遍历到i,这块别写错了,假如拆10的话,最多就是1和9。
5.模拟dp数组结果
这一步我通常是省略的,先提交程序,没通过或者输出了再回来debug~

细节:
为什么只拆了i-j 不考虑拆j?

这一点其实从遍历顺序里就能看出来,第二层的for 里 j从1遍历到了i,所以j其实是已经从1拆分到了i的,所以不需要考虑拆分他了。

完整代码

class Solution:
    def integerBreak(self, n: int) -> int:
        dp = [0] * (n+1)
        dp[0] = dp[1] = 0
        for i in range(2,n+1):
            for j in range(1,i):
                dp[i] = max(dp[i],j*(i-j),j*dp[i-j])
        return dp[-1]

数学

我在评论看到一个用数学方法的,总之证明一大堆,我这里直接用结论~每次拆成n个3,如果剩下是4,则保留4,然后相乘,

这个方法其实就是贪心嘛,感觉比dp容易。

class Solution:
    def integerBreak(self, n: int) -> int:
        res = 1
        if n == 2:
            return 1
        if n ==3 :
            return 2
        if n == 4:
            return 4
        while n > 4:
            res *=3
            n -=3
        res *= n
        return res
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深度不学习!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值