343. 整数拆分
文档讲解:代码随想录 (programmercarl.com)
视频讲解:动态规划,本题关键在于理解递推公式!| LeetCode:343. 整数拆分_哔哩哔哩_bilibili
状态:做不出来。应该算难题了。注意这题是从两层for,而之前题目都是一层for,这是题目的难点。真算是一层for到两层for过渡的经典题。
思路
先看视频后看文档,文档不够细。
动规五部曲,分析如下:
-
确定dp数组以及下标的含义
dp[i]:分拆数字i,可以得到的最大乘积为dp[i]。dp[i]的定义将贯彻整个解题过程。
-
确定递推公式
dp[i]是怎么得到的呢?
令j从1遍历到 i-1,有两种渠道得到dp[i]:
①将i拆分成两个数字j 和i-j,那么dp[i] = j * (i - j);
②将i拆分成两个以及两个以上的个数相乘,那么dp[i] = j * dp[i - j],相当于是拆分(i - j),dp[i - j]表示对(i - j)拆分相乘得到的最大值;
在j从1遍历到 i-1过程中,比较 j * (i - j) 和 j * dp[i - j],取最大的,递推公式为dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
在取最大值的时候,为什么还要比较dp[i]呢?因为在递推公式推导的过程中,每次计算dp[i],取最大的而已,比如对数字10拆分,可拆成很多种:5、5,3、3、4,…要从里面选最大的。
那有同学问了,j怎么就不拆分呢?
比如 i=6,dp[6] = 2 x dp[4] = 2 x 1 x 1 x 1 x1,此时j=2,那么有个问题:为什么不拆分j,即为什么不拆分2?
这是因为之前 j=1 时,dp[6] = 1 x dp[5] = 1 x 1 x 1 x 1 x 1已经出现过把2拆分的情况了,所以是为了不重复拆分。
-
dp的初始化
严格从dp[i]的定义来说,dp[0] dp[1] 就不应该初始化,也就是没有意义的数值。
只初始化dp[2] = 1,从dp[i]的定义来说,拆分数字2,得到的最大乘积是1,这个没有任何异议!
-
确定遍历顺序
先来看看递推公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
dp[i] 是依靠 dp[i - j]的状态,所以遍历i一定是从前向后遍历,先有dp[i - j]再有dp[i]。
for (int i = 3; i <= n ; i++) { //遍历每个数i拆分后的最大乘积 for (int j = 1; j < i; j++) { //把i拆成两部分,第一部分为固定值j,第二部分为(i-j)或dp[i-j] dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j)); } }注意 枚举j的时候,是从1开始的。从0开始的话,那么让拆分一个数拆个0,求最大乘积就没有意义了。
至于 i是从3开始,这样dp[i - j]就是dp[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)); } }因为拆分一个数n 使之乘积最大,那么一定是拆分成m个近似相同的子数相乘才是最大的。
例如 6 拆成 3 * 3, 10 拆成 3 * 3 * 4。 100的话 也是拆成m个近似数组的子数 相乘才是最大的。
只不过我们不知道m究竟是多少而已,但可以明确的是m一定大于等于2,既然m大于等于2,也就是 最差也应该是拆成两个相同的 可能是最大值。
那么 j 遍历,只需要遍历到 n/2 就可以,后面就没有必要遍历了,一定不是最大值。
-
举例推导dp数组
代码
class Solution {
public:
int integerBreak(int n) {
vector<int> dp(n + 1);
dp[2] = 1;
for(int i = 3; i <= n; i++){ //遍历每个数i拆分后的最大乘积
for(int j = 1; j <= i/2; j++){ //把i拆成两部分,第一部分为固定值j,第二部分为(i-j)或dp[i-j]
dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]));
}
}
return dp[n];
}
};
96.不同的二叉搜索树
文档讲解:代码随想录 (programmercarl.com)
视频讲解:动态规划找到子状态之间的关系很重要!| LeetCode:96.不同的二叉搜索树_哔哩哔哩_bilibili
状态:不会做。感觉很难。
思路
! 求多少种、最值,几乎都可以往dp靠。
直接看视频吧,这题没做过很难想到递推公式。如果要找递归公式,先从n=1,2,3把图画出,看里面的规律,即利用dp[1]和dp[2]推导出dp[3]。
class Solution {
public:
int numTrees(int n) {
vector<int> dp(n + 1);
dp[0] = 1;
for(int i = 1; i <= n; i++){ //遍历各个dp[i]
for(int j = 1; j <= i; j++){ //每个dp[i]的构建过程
dp[i] += dp[j - 1] * dp[i - j];
}
}
return dp[n];
}
};
1321

被折叠的 条评论
为什么被折叠?



