文章目录
- 343. 整数拆分
- 思路
- 代码
- 96.不同的二叉搜索树
- 思路
- 代码
343. 整数拆分
题目链接:343. 整数拆分
文章讲解:代码随想录|343. 整数拆分
视频讲解:整数拆分
思路
1.dp[i]:整数i拆分成k个数的最大乘积
2.dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j)):
从1遍历j,比较(i - j) * j和dp[i - j] * j 取最大的
(i - j) * j是拆成两个数,dp[i - j] * j 是拆成3个或更多
dp[i - j]肯定比dp[i]要小,之前已经求出来了
比如数字10,可以拆成
1x9
2x8
3x7,7可以拆成3x4等等,4可以拆成2x2等等(不拆3x7中的3是因为拆3的情况肯定在拆1x9和2x8的时候出现过)
4x6
5x5
3.dp[2] = 1
4.从前向后遍历
5.举例
代码
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((i - j) * j, dp[i - j] * j));
}
}
return dp[n];
}
};
j为什么不从i/2 开始遍历:因为当需要拆成3个及以上的数值相似的数时,j就小于i/2了
96.不同的二叉搜索树
题目链接:96.不同的二叉搜索树
文章讲解:代码随想录|96.不同的二叉搜索树
视频讲解:96.不同的二叉搜索树
思路
一开始看比较懵,可以先举几个例子,看看有没有规律
当1为头结点的时候,其右子树有两个节点,看这两个节点的布局,是不是和 n 为2的时候两棵树的布局是一样的啊!
(可能有同学问了,这布局不一样啊,节点数值都不一样。别忘了我们就是求不同树的数量,并不用把搜索树都列出来,所以不用关心其具体数值的差异)
当3为头结点的时候,其左子树有两个节点,看这两个节点的布局,是不是和n为2的时候两棵树的布局也是一样的啊!
当2为头结点的时候,其左右子树都只有一个节点,布局是不是和n为1的时候只有一棵树的布局也是一样的啊!
发现到这里,其实我们就找到了重叠子问题了,其实也就是发现可以通过dp[1] 和 dp[2] 来推导出来dp[3]的某种方式。
dp[3],就是 元素1为头结点搜索树的数量 + 元素2为头结点搜索树的数量 + 元素3为头结点搜索树的数量
元素1为头结点搜索树的数量 = 右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量
元素2为头结点搜索树的数量 = 右子树有1个元素的搜索树数量 * 左子树有1个元素的搜索树数量
元素3为头结点搜索树的数量 = 右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量
有2个元素的搜索树数量就是dp[2]。
有1个元素的搜索树数量就是dp[1]。
有0个元素的搜索树数量就是dp[0]。
所以dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2]
- dp[i] : 1到i为节点组成的二叉搜索树的个数为dp[i]
- 所以递推公式:dp[i] += dp[j - 1] * dp[i - j]; j相当于是头结点的元素,从1遍历到i为止。j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量
- dp[0] = 1
- 遍历i里面每一个数作为头结点的状态,用j来遍历。
- 举例
代码
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];
}
};