343.整数拆分
给定一个正整数 n
,将其拆分为 k
个 正整数 的和( k >= 2
),并使这些整数的乘积最大化。
返回 你可以获得的最大乘积 。
思路
对于正整数n当n>=2时,可以拆分成至少两个正整数的和,令x是拆分出的第一个正整数,剩下的部分是n-x,n-x可以不继续拆分,或者继续拆分成至少两个正整数的和。由于每个正整数对应的最大乘积取决于比它小的正整数对应的最大乘积,因此可以使用动态规划求解。
动态规划法
创建数组dp,其中dp[i]表示将整数i拆分成若干个正整数的和之后,这些正整数的最大乘积。0不是正整数,1是最小的正整数,都不能拆分,所以dp[0],dp[1]初始化为0。
当i>=2时,假设对i拆分出的第一个数为j,则有两种方案:
1、将i拆分成j和i-j的和,且i-j不再拆分为多个正整数,此时乘积为j*(i-j)
2、将i拆分成j和i-j的和,且i-j继续拆分为多个正整数,此时乘积为j*dp[i-j]
因此,当j固定时,有dp[i]=max{j*(i-j),j*dp[i-j]},由于j的取值范围为1到i-1,所以需要遍历所有j的可能取值找到dp的最大值,可以得出dp的状态转移方程如下:
dp[i]=(1≤j<i)max{max(j×(i−j),j×dp[i−j])}.
代码
class Solution {
public int integerBreak(int n) {
int[] dp=new int[n+1];
//dp[0]=dp[1]=0;
for(int i=2;i<=n;i++){
int curMax=0;
for(int j=1;j<i;j++){
curMax=Math.max(curMax,Math.max((i-j)*j,dp[i-j]*j));
}
dp[i]=curMax;
}
return dp[n];
}
}
动态规划优化
看了半天题解,手写了半天推导,还是放弃了,不知道这种优化第一次做怎么可能想得出来数学证明。
主要思路就是,只需要考虑j=2和j=3的情况。
代码
class Solution {
public int integerBreak(int n) {
if (n <= 3) {
return n - 1;
}
int[] dp = new int[n + 1];
dp[2] = 1;
for (int i = 3; i <= n; i++) {
dp[i] = Math.max(Math.max(2 * (i - 2), 2 * dp[i - 2]), Math.max(3 * (i - 3), 3 * dp[i - 3]));
}
return dp[n];
}
}
贪心(数学法)
代码
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) Math.pow(3, quotient);
} else if (remainder == 1) {
return (int) Math.pow(3, quotient - 1) * 4;
} else {
return (int) Math.pow(3, quotient) * 2;
}
}
}
96.不同的二叉搜索树
给你一个整数 n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
思路
代码
class Solution {
public int numTrees(int n) {
int[] dp=new int[n+1];
dp[1]=dp[0]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i]+=dp[j-1]*dp[i-j];
}
}
return dp[n];
}
}