题目描述(中等)
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
说明: 你可以假设 n 不小于 2 且不大于 58。
思路1:动态规划
首先把边界判断掉,2返回2,3返回3;
对于后面的数,通过观察,4时的结果是13,112,1111,22,211,31中的最大值
令1时最优解为1,2时最优解为2,3时最优解为3,那4的最优解相当于13,22,31中最大值,即4
对于5,其结果为1(4时最优解),2*(3时最优解),3*(2时最优解),4*(1时最优解)中的最大值,即6
对于6,其结果为1*(5时最优解),2*(4时最优解),3*(3时最优解),4*(2时最优解),5*(1时最优解)中的最大值,即9
后面的结果由前面的结果运算得到,且前面结果不受后面数的影响,动态规划实锤
dp[i]定义为i时最优解(i>=4)
对于数n,将其拆分为至少两个正整数的和,我们假设拆分为x+y,如果要继续拆分y,那此时结果为xdp[y];如果要继续拆分x,同理ydp[x];如果都要拆分,问题其实就是将n拆分为x1和(x1+y1+y2)这样的组合。
所以遍历n拆分为两项的所有组合,即可考虑所有组合
递推公式为dp[i] = max(dp[i],dp[j]*(i-j));其中j从i-1到1遍历
由于要乘积最大,所以拆分组合带1肯定不会是最优解,j可以从i-2到2进行遍历,减少不必要计算。
需要注意的就是,边界值并不是dp里面的最优解,虽然2时最优解为1,但dp[2]初始为2;dp[3]同理
代码
class Solution {
public:
int integerBreak(int n) {
vector<int> dp(n+1,0);
if(n == 2) return 1;
if(n == 3) return 2;
dp[1] = 1;
dp[2] = 2;
dp[3] = 3;
for(int i = 4; i <= n; i++) {
for(int j = i-2; j > 1; j--) {
dp[i] = max(dp[i],dp[j]*(i-j));
}
}
return dp[n];
}
};
思路2:数学
复杂的公式推导详见官方题解
比较清晰的推导过程详见大佬题解
主要是均值不等式的运用,当每个拆分的小块取
x
1
x
x^{\frac{1}{x}}
xx1时乘积才能最大,而
x
1
x
x^{\frac{1}{x}}
xx1的极大值取在e=2.71828…
而本题只能取整数,所以就在2、3之间,而且口算可知
3
∗
3
>
2
∗
2
∗
2
3*3>2*2*2
3∗3>2∗2∗2,所以能选3就选3,选不了3就选2。
其实自己写的过程中也发现,最大的结果都是几个2、3的相乘。
数学思路的简单推理:
对于 n >= 4,拆分后总是收益更大,因此最终所有 >= 4 的数都可以拆分为 2,3的组合(当然不可能有1)。
而 2*2*2 < 3*3 ,2*2 > 3*1 ;因此拆分后所有的 2 三个一组合并为 3*3,剩下的2保留,最后计算乘积即可。
代码
class Solution {
public:
int integerBreak(int n) {
if (n <= 3) {
return n - 1;
}
int quotient = n / 3;
int remainder = n % 3;
if (remainder == 0) {
return (int)pow(3, quotient);
} else if (remainder == 1) {
return (int)pow(3, quotient - 1) * 4;
} else {
return (int)pow(3, quotient) * 2;
}
}
};