day41
343.整数拆分
题目链接:343. 整数拆分
拆分的原则是尽可能拆分成相同的数,这样最终的结果才能保持最大,这在之后的代码优化里面有体现,具体为j <= i/2
动态规划
动态规划五部曲:
1.dp数组及下标的含义:对i进行拆分得到的最大的乘积值为dp[i]
2.递推公式:$j * (i - j)$针对两个数的乘积,$j * dp[i - j]$针对三个及以上的数的乘积,最终所有的结果取最大值
dp[i] = max(j * dp[i-j], dp[i]); //还需要和dp[i]本身取最大值 dp[i] = max( j * (i - j ), dp[i]);
这里注意拆分$j$和拆分$i-j$是一样的情况。
3.递推公式初始化
dp[0] = 0; //无意义 dp[1] = 0; //无意义 dp[2] = 1; //初始化为1
4.遍历顺序:从前向后,从
i = 3
开始5.打印dp数组,用来调bug
注意一个细节:dp下标从0开始最后到n,所以在dp初始化的时候需要初始化大小为$n+1$
class Solution {
public:
// 时间:O(n^2)
// 空间:O(n)
int integerBreak(int n) {
vector<int> dp(n+1,0); //定义dp数组
dp[0] = 0; //这个无意义,可以不写
dp[1] = 0; //这个无意义,可以不写
dp[2] = 1;
for( int i = 3; i <= n; i++){
for( int j = 1; j <= i/2; j++){
dp[i] = max(j * dp[i-j], dp[i]);
dp[i] = max( j * (i - j ), dp[i]);
}
}
return dp[n];
}
};
贪心
思路:每次拆分成n个3,如果剩下的是4,则保留4,然后相乘。这个是思路,具体原因需要数学证明。
class Solution {
public:
int integerBreak(int n) {
int result = 1;
if( n == 1 ) return 0;
if( n == 2 ) return 1;
if( n == 3 ) return 2;
while( n > 4 ){
result *= 3;
n -= 3;
}
result *= n;
return result;
}
};
96.不同的二叉搜索树
题目链接:96. 不同的二叉搜索树
1.确定dp数组以及下标i的含义:dp[i]种不同的二叉搜索树
2.确定递推公式
dp[i] += dp[j - 1] * dp[i - j];
上面$dp[j-1]$统计的是第j个二叉树的左子树的数量,$dp[i-j]$统计的是第j个二叉树的右子树的数量
3.初始化:$dp[0] = 1;$ 空二叉树也可以是二叉搜索树
4.遍历顺序:从前向后遍历,初始是从1开始的
for( int i = 1; i <= n; i++){ for( int j = 1; j <= i; j++){ } }
5.打印数组
class Solution {
public:
int numTrees(int n) {
vector<int> dp(n+1,0);
dp[0] = 1; // dp数组初始化
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];
}
};