今日任务:
343. 整数拆分
- 96.不同的二叉搜索树
一、整数拆分
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
(困)
还是按东归五部曲走吧:1、dp数组及下标的含义
本题中dp[i]是指对于数字i拆分的最大乘积。
2、确定递推公式
得到dp[i]有两种方式,分别对应只拆分一次和拆分多次,
只拆分一次:j*(i-j)
拆分多次:j*dp[i-j],这个代表拆分多次是因为联系到了前面的值,而前面是拆分过的了。
3、dp初始化
dp[0],dp[1]的值是无效值,因为他们无法进行拆分为正整数和,所以可以直接对dp[2]进行初始化,dp[2]=1。
4、遍历顺序,由于后面的最大值可能取决于前面的最大值,所以应该从前向后遍历。
这里的遍历可以优化,毕竟j过了i/2之后就和前面的重合了。
可以这么写:
for (int i = 3; i <= n ; i++) {
for (int j = 1; j <= i / 2; j++) {
dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
}
}
5、举例推导dp数组
看着还挺简单(狗头)
class Solution {
public:
int integerBreak(int n) {
vector<int> dp(n+1);
dp[2] =1;
for(int i =3;i<=n;i++){
for(int j=1;j<=i/2;j++){
dp[i] =max(dp[i],max(j*(i-j),dp[i-j]*j));
}
}
return dp[n];
}
};
但这题这里不能只维护两个元素了,因为前面的任何值可能被用到,无法确定该维护多少。
贪心方法:
当分解式中拆分成4以上时,比如5,那么再次拆分为 2 * 3 = 6,结果显然大于5;所以,根据数学推理,很容易得出,分解式中3越多越好,这样乘积才会越来越大;当只分解式中含有4的时候,拆分成2 * 2,则更好。故:分解式应该尽可能的拆分成更多的3。当只剩下4的时候,就直接取4就是最大值。
class Solution {
public:
int integerBreak(int n) {
if(n==2) return 1;
if(n==3) return 2;
if(n==4) return 4;
int result =1;
while(n>4){
result*=3;
n-=3;
}
result *=n;
return result;
}
};
二、不同的二叉搜索树
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
并不是很理解,要按动归五部曲来走一遍
1、确定dp数组以及下标含义
dp[i]是i个节点能组成的组成二叉搜索树的个数。
关于二叉搜索树,可以看这里:
(43条消息) 【数据结构】——二叉搜索树_努力学习的少年的博客-CSDN博客
2、确定递推公式
dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]
j相当于是头结点的元素,从1遍历到i为止。
所以递推公式:dp[i] += dp[j - 1] * dp[i - j];
3、dp数组如何初始化
dp[0]=1
由于要做乘法的乘数之一。
4、遍历顺序
其值依赖于前面节点数的状态所以从前向后遍历
5、第五步:举例推导
class Solution {
public:
int numTrees(int n) {
vector<int> dp(n+1);
dp[0] =1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i] +=dp[j-1]*dp[i-j];
}
}
return dp[n];
}
};