343.整数拆分
题目链接/文章讲解/视频讲解:代码随想录
1.代码展现
//343整数拆分
int integerBreak(int n) {
//step1 构建dp数组,确定dp[i]的含义
//dp[i]的含义是i被拆分后的积的最大值
vector<int> dp(n + 1, 0);
//step2 确定递推公式
//dp[i] = max(dp[i], max(j * (i - j), j * dp(i - j)))
//step3 dp初始化,dp[0], dp[1]初始化没有意义,不能被拆分成正整数
//根据递推公式,只需要初始化dp[2]
dp[2] = 1;
//step4 遍历
for (int i = 3; i <= n; i++) {
for (int j = 1; j < i; j++) {
dp[i] = max(dp[i], max(dp[i - j] * j, (i - j) * j));
}
}
return dp[n];
}
2.本题小节
思考:首先要明确递推公式的由来,本题的核心就是将一个数字拆成两个数字,然后进行乘积,被拆得数字还可以继续拆,直到不能拆为止,观察递推公式可以看出,在拆分得过程中供dp[i]选择得情况有三种:选择dp[i],证明这次拆分的两种结果都不及dp[i]大,这种情况一般出现在拆分的后半段,dp[i]已经获得最大值时;选择dp[i - j] * j,说明i被拆成了三个即三个以上的数, 如10被拆成了3,3,4,这情况就是dp[7] * 3,dp[7] 最大值是被拆成3 * 4 时;选择(i - j)* j时,如dp[4],为2 * 2。当然这几种情况都是举例子,实际上也会出现dp[i - j] * j = (i - j) * j的情况。由于i需要拆分为正整数,因此初始化时i = 0 和 i = 1的情况是不存在的,没有意义。
基本思路:把握好递归公式的由来和遍历顺序本题就比较好做了,跟着五部曲来。
96.不同的二叉搜索数
题目链接/文章讲解/视频讲解:代码随想录
1.代码展现
//96 不同的二叉搜索树
int numTrees(int n) {
//step1 构建dp数组
//dp[i]的意义是i个节点组成的二叉树的个数
vector<int> dp(n + 3, 0);
//step2 推导递推公式
//dp[i] += dp[j] * dp[i - j - 1]
//step3 初始化dp数组
dp[0] = 1;
dp[1] = 1;
dp[2] = 2;
if (n <= 2) {
return dp[n];
}
//step4 开始遍历
for (int i = 3; i <= n; i++) {
for (int j = 0; j < i; j++) {
dp[i] += dp[j] * dp[i - j - 1];
//step5 打印数组
}
}
return dp[n];
2.本题小节
思考:本题的递推公式比较难想到,这个递推公式还是要多多理解,就拿n = 3 来说,当1为根节点时,左边节点个数为0,右边节点个数为2,此时左边的节点分布和dp[0]一样,右边的节点分布情况和dp[2]一样,因此能够组成的情况为dp[0] * dp[2],当2、3为根节点同理,由此可以推导出递推公式。dp数组初始化时注意指针越界,因此容器最小设置为3。
基本思路:理解递推公式的推导以及遍历顺序,跟着五步走即可。