文章目录
一、题目描述
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
说明:你可以假设 n 不小于 2 且不大于 58。
二、解题思路
对于的正整数 n,当n≥2 时,可以拆分成至少两个正整数的和。令 k 是拆分出的第一个正整数,则剩下的部分是 n-k,n−k 可以不继续拆分,或者继续拆分成至少两个正整数的和。由于每个正整数对应的最大乘积取决于比它小的正整数对应的最大乘积,因此可以使用动态规划求解。
创建数组dp,其中dp[i] 表示将正整数 i 拆分成至少两个正整数的和之后,这些正整数的最大乘积。特别地,0不是正整数,1是最小的正整数,0 和 1 都不能拆分,因此 dp[0]=dp[1]=0。
当 i ≥ 2 时,假设对正整数 i 拆分出的第一个正整数是 j( 1≤ j< i),则有以下两种方案
-
将 i 拆分成 j 和 i−j 的和,且 i−j 不再拆分成多个正整数,此时的乘积是 j × (i−j);
-
将 i 拆分成 j 和 i−j 的和,且 i−j 继续拆分成多个正整数,此时的乘积是 j ×dp[i−j]。
当 j 固定时,有 dp[i]=max( j x (i-j), j x dp[i−j])。由于 j 的取值范围是 1 到 i−1,需要遍历所有的 j 得到 dp[i] 的最大值,因此可以得到状态转移方程如下:
最终得到dp[n] 的值即为将正整数 n 拆分成至少两个正整数的和之后,这些正整数的最大乘积。
然后根据上面的分析代入动规五步曲:
第一步:确定dp数组的含义
dp[i] 表示将 i 拆分之后的数的最大乘积。
第二步:确定递推公式
上面的分析中已经确定。
第三步:dp初始化
根据上面分析:dp[0]=dp[1]=0,其实严格来说是没有意义的数值,可以从直接从dp[2]=1初始化就可以。
第四步:确定遍历顺序
dp[i] 是依靠 dp[i - j]的状态,所以遍历i⼀定是从前向后遍历,先有dp[i - j]再有dp[i]。枚举j的时候,是从1开始的。i是从3开始,这样dp[i - j]就是dp[2]正好可以通过我们初始化的数值求出来,认真理解一下为什么j<i-1.
for (int i = 3; i <= n ; i++) {
for (int j = 1; j < i - 1; j++) {
dp[i] =Math.max(dp[i], Math.max((i-j) * j, dp[i-j] * j));
}
}
第五步:举例推导dp数组
举例当n为10 的时候,dp数组⾥的数值,如下:
二、代码演示
class Solution {
public int integerBreak(int n) {
int[] dp = new int[n+1];
//数组初始化
dp[2] = 1;
for(int i=3; i<n+1; i++){
for(int j=1; j<i-1; j++){
dp[i] = Math.max(dp[i],Math.max(j*(i-j), j*dp[i-j]));
}
}
return dp[n];
}
}
时间复杂度:O(n^2)
空间复杂度:O(n)