文章目录
- 题目描述
- 思路分析
- 完整代码
- 数学
题目描述
给定一个正整数 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的,所以不需要考虑拆分他了。
完整代码
数学
我在评论看到一个用数学方法的,总之证明一大堆,我这里直接用结论~每次拆成n个3,如果剩下是4,则保留4,然后相乘,
这个方法其实就是贪心嘛,感觉比dp容易。