整数拆分
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
例如:输入10,输出36。10 可以 拆分成3 3 4, 3 * 3 * 4 = 36。
分析案例
- 10 拆成 3、3、4 时是最好的解。而这其中涉及了两步拆解。
- 10 可以先拆成 6、4,由 6 再去拆解成 3、3,10 也可以先拆成 3 、7,由7再去拆成3、4
算法
- 由上述分析得知,任何一个数可以先拆成两个,然后由其中一个再去进行拆分
为什么是其中一个,不需要两个数字一起拆分?
- 再拿10来分析,10 可以拆成5 和 5,那么应该5和5一起拆分啊。因为 5 * 2 * 3 < 2 * 3 * 2 * 3啊,这样不就是漏掉了一些可能了吗?
- 目前看来好像是应该两个都拆分,其实两个都拆分也没错,只是多余而已。因为2 * 3 * 2 *3 可以看成把10 拆成 2 * 8,而 2 * 8这种情况,我们一定也会计算到的。
定义状态和状态转移方程式
- 定义 dp[n] :当前的拆分完后的最好的值。
- 方程转移:dp[n] = Max(Max((j∈ 1 到 N) * N - j) ,(j∈ 1 到 N) * dp[N - j]),dp[n])
- 注意:状态转移方程需要对比一下 dp[n] 因为dp[n] 可分割为多种组合
附上代码:
public int integerBreak(int n) {
// 动态规划 dp[i]:当处于i时,拆分了至少两个以上的最大乘积
// 第一步:定义 i ,i代表 2 .... n 的循环,所以 i 可分割为 两个数,定分割点为 j。
// 所以,dp[i] 可以是 j * (i - j),而这里有一个很关键的地方是,j 和 (i - j)是可以继续往下分割的。
// 而 j 和 i - j 只需要考虑i - j即可,因为把j继续往下拆分的情况已经考虑过了,请看上述的分析
// 综上所述 状态转移方程为:dp[i] = max(j * (i - j),j * dp[i-j]);
// 第二步:dp[n] 即是答案。
int[] dp = new int[n + 1];
for (int i = 2; i <= n; i++){//记住 需要包括n 因为n代表是一个数字 而不是下标
//遍历分割点
for(int j = 1; j < i;j++){
dp[i] = Math.max(Math.max(j * (i - j), j * dp[i - j]),dp[i]);
}
}
return dp[n];
}