题目描述:给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你能够得到的最大乘积。javascript
题目中“n 至少能够拆分为两个正整数的和”,这个条件说明了 n 是大于 1 的整数。java
对 7 来讲,能够拆成 3+4,最大乘积是 12。git
对 8 来讲,能够拆成 3+3+2,最大乘积是 18。github
状态数组dp[i]
表示:数字 i 拆分为至少两个正整数之和的最大乘积。为了方便计算,dp 的长度是 n + 1,值初始化为 1。算法
显然dp[2]
等于 1,外层循环从 3 开始遍历,一直到 n 中止。内层循环 j 从 1 开始遍历,一直到 i 以前中止,它表明着数字 i 能够拆分红 j + (i - j)。但 j * (i - j)
不必定是最大乘积,由于i-j
不必定大于dp[i - j]
(数字i-j
拆分红整数之和的最大乘积),这里要选择最大的值做为 dp[i]
的结果。数组
空间复杂度是 \(O(N)\),时间复杂度是 \(O(N^2)\)。代码实现以下:spa
// ac地址:https://leetcode-cn.com/problems/integer-break/ // 原文地址:https://xxoo521.com/2020-02-15-integer-break/ /** * @param {number} n * @return {number} */ var integerBreak = function(n) { const dp = new Array(n + 1).fill(1); for (let i = 3; i <= n; ++i) { for (let j = 1; j < i; ++j) { dp[i] = Math.max(dp[i], j * (i - j), j * dp[i - j]); } } return dp[n]; };
力扣上此题给出了提示:多试试几个例子,找出规律。下面说下我找规律的思路。.net
前面提到:8 拆分为 3+3+2,此时乘积是最大的。而后就推测出来一个整数,要拆成多个 2 和 3 的和,保证乘积最大。原理很容易理解,由于 2 和 3 能够合成任何数字,例如5=2+3
,可是5 < 2*3
;例如6=3+3
,可是6<3*3
。因此根据贪心算法,就尽可能将原数拆成更多的 3,而后再拆成更多的 2,保证拆出来的整数的乘积结果最大。code
但上面的解法还有不足。若是整数 n 的形式是 3k+1,例如 7。按照上面规则,会拆分红“3 + 3 + 1”。可是在乘法操做中,1 是没做用的。此时,应该将 1 和 3 变成 4,也就是“3 + 3 + 1”变成“3 + 4”。此时乘积最大。blog
综上所述,算法的总体思路是:
空间复杂度是 O(1),时间复杂度是 O(1)。代码实现以下:
// ac地址:https://leetcode-cn.com/problems/integer-break/ // 原文地址:https://xxoo521.com/2020-02-15-integer-break/ /** * @param {number} n * @return {number} */ var integerBreak = function(n) { if (n === 2) return 1; if (n === 3) return 2; // a的含义:n能拆成的3的个数 const a = Math.floor(n / 3); const b = n % 3; // n是3的倍数 if (b === 0) return Math.pow(3, a); // n是 3k + 1,例如7。拆成三、三、1。因为有1对结果没法有贡献,因此最后的三、1换成4 if (b === 1) return Math.pow(3, a - 1) * 4; return Math.pow(3, a) * 2; };
若是想了解详细的数学推理,请参考《Leetcode 343:整数拆分(最详细的解法!!!)》。