目录
- 递归
- 动态规划
题目来源
343. 整数拆分
递归
对于给定的一个整数 n,穷举它的每一种分解情况,然后对所有情况,求最大值。 并且我们知道,n 可以拆成如下情况:
通过上图,我们很容易得到一个递归表达式:
F(n)=max{i∗F(n−i)},i=1,2,...,n−1
上述表达式是表明n - i需要继续分解的情况,但如果n - i比F(n - i)要大,显然就不用再继续分解了。故我们还需要比较i * (n - i)与i * F(n - i)的大小关系。所以完整的表达式应该为:
F(n)=max{i∗F(n−i), i∗(n−i)},i=1,2,...,n−1
class Solution {
public int integerBreak(int n) {
if(n == 2){
return 1;
}
int res = -1;
for(int i = 1; i <= n-1;i++){
res = Math.max(res,Math.max(i*(n-i),i*integerBreak(n-i)));
}
return res;
}
}
动态规划
- 1.确定dp数组(dp table)以及下标的含义
dp[i]:分拆数字i,可以得到的最大乘积为dp[i]。
- 2.确定递推公式
可以想 dp[i]最大乘积是怎么得到的呢?
其实可以从1遍历j,然后有两种渠道得到dp[i].
一个是j * (i - j) 直接相乘。
一个是j * dp[i - j],相当于是拆分(i - j),对这个拆分不理解的话,可以回想dp数组的定义。
j怎么就不拆分呢?
j是从1开始遍历,拆分j的情况,在遍历j的过程中其实都计算过了。那么从1遍历j,比较(i - j) * j和dp[i - j] * j 取最大的。递推公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
j * (i - j) 是单纯的把整数拆分为两个数相乘,而j * dp[i - j]是拆分成两个以及两个以上的个数相乘。
- 3.dp的初始化
只初始化dp[2] = 1,从dp[i]的定义来说,拆分数字2,得到的最大乘积是1
- 4.确定遍历顺序
确定遍历顺序,先来看看递归公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
dp[i] 是依靠 dp[i - j]的状态,所以遍历i一定是从前向后遍历,先有dp[i - j]再有dp[i]。
for(int i = 3;i<=n;i++){
for(int j = 1;j < i-1;j++){
dp[i] = Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));
}
j的结束条件是 j < i - 1 ,其实 j < i 也是可以的,不过可以节省一步,例如让j = i - 1,的话,其实在 j = 1的时候,这一步就已经拆出来了,重复计算,所以 j < i - 1
至于 i是从3开始,这样dp[i - j]就是dp[2]正好可以通过我们初始化的数值求出来。
- 5.举例推导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;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];
}
}