343. 整数拆分
题目链接
思路
- 确定dp数组以及下标的含义
dp[i]:分拆数字i,可以得到的最大乘积为dp[i] - 递推公式
可以从1遍历j,然后有两种渠道得到dp[i].
一个是j * (i - j) 直接相乘
一个是j * dp[i - j],相当于是拆分(i - j)
dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j))
为什么要这样拆分呢?因为(i - j)* j默认为拆成两个数,而拆分成n个相近的数才能让乘积最大。j * dp[i - j] 默认为拆成多个数,但如果是拆成dp[j]*dp[i - j]默认为拆成4个及4个数以上。
再细化一点来说,当整数值比较小的时候,一般拆成1、2、3这三个基本值。1不能让乘积变大,尽量不拆成1;22=4,但23=6,虽然一个乘法因子的值只增加1,但乘法值增加了2,所以在拆分时,应该拆成尽量多的3,剩下的可以拆成2,而任何一个大于3的数都可以用2和3来组成。当用 dp[i-j]j 时,dp[i-j] 是已经通过不断叠加2和3来取得的最大值,肯定是没有问题的;但用dp[j]dp[i-j] 时,当j=3,dp[j]却是又有可能被强制拆成了1、2这样的数,倒不如直接取3 - 初始化
dp[0]和dp[1]是没有意义的初始化,初始化dp[2]=1才是有意义的 - 遍历顺序
dp[i] 是依靠 dp[i - j]的状态,所以遍历i一定是从前向后遍历 - 举例推导dp数组
代码实现
class Solution {
public:
int integerBreak(int n) {
if(n<=1)return 0;
vector<int> dp(n+1);
dp[2]=1;
for(int i=3;i<=n;i++){
for(int j=1;j<=i-1;j++){
dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]));
}
}
return dp[n];
}
};
96.不同的二叉搜索树
题目链接
思路
- 确定dp数组(dp table)以及下标的含义
dp[i]:n 个节点组成且节点值从1到n可以组成dp[i]种搜索二叉树 - 确定递推公式
二叉搜索树特性绝对二叉树每个结点的左子树值都比根节点小,左子树值都比根节点大,所以当选择1-n中的数字i时,左子树结点有 i-1 个,右子树结点有 n-i 个,所以选取每个结点而构成的二叉搜索树有 dp[ i-1 ] * dp[ n-i ] 个,则递推公式为 dp[i]+=dp[j-1]*dp[i-j],其中j取1-n - dp数组如何初始化
只需要初始化前两个则可以递推得到后面的。dp[0]=1;dp[1]=1; - 确定遍历顺序
由递推公式可知是从前往后递推 - 举例推导dp数组
代码实现
class Solution {
public:
int numTrees(int n) {
if(n<2)return 1;
vector<int> dp(n+1);
dp[0]=1;
dp[1]=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];
}
};