-
343整数拆分
-
第一印象
- 使得拆分后乘积较大的方法是尽可能等分当前的数,若拆分后下一层数字大于4则可以再一次拆分。但这样是需要迭代判断的方法,代码构建比较繁琐。
-
讲解观后感
- 按照动规解题的思想。首先就是确定dp数组的含义,其中
dp[i]一定是代表当前的最大乘积(即题目答案)。
- 然后是dp的推导公式,
dp[i]的获取一个是j * (i - j) 直接相乘。一个是j * dp[i - j],相当于是拆分(i - j)。所以递推公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
- 初始化
dp[2] = 1
- dp[i] 是依靠 dp[i - j]的状态,所以遍历i一定是从前向后遍历
-
解题代码
-
func integerBreak(n int) int {
dp := make([]int, n+1)
dp[1] = 1
dp[2] = 1
for i := 3; i < n+1; i++ {
for j := 1; j < i-1; j++ {
// i可以差分为i-j和j。由于需要最大值,故需要通过j遍历所有存在的值,取其中最大的值作为当前i的最大值,在求最大值的时候,一个是j与i-j相乘,一个是j与dp[i-j].
dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]))
}
}
return dp[n]
}
func max(a, b int) int{
if a > b {
return a
}
return b
}
- 贪心C++
- 数学原理需要自己推导
-
class Solution {
public:
int integerBreak(int n) {
if (n == 2) return 1
if (n == 3) return 2
if (n == 4) return 4
int result = 1
while (n > 4) {
result *= 3
n -= 3
}
result *= n
return result
}
}
-
不同的二叉搜索树
- 代码随想录 (programmercarl.com)
-
第一印象
- 确定
dp[i]代表n=1时有多少不同的二叉搜索树。
- 因为是二叉搜索树,所以确定根节点后,可以明确的知道左右子树是哪些数字,并且左右子树的种类相乘就是这个根情况的种类。所以我们只要遍历不同的根节点的情况进行相加即可。
- 使用
j:=1;j<=i;j++进行遍历即可,得到
-
for j:=1;j<=i;j++ {
dp[i] += dp[j-1] * dp[i-j]
}
- 易得
dp[1], dp[2] = 1, 2,以及当没有数字时可视为一种情况,所以dp[0] = 1。
- 从前向后遍历即可。
-
讲解观后感
- 自己一次手撸成功的第一个动规题目,方法和卡尔的思路几乎一摸一样,只不过没有其简洁,原因是没有尽量使递推公式覆盖。但总体差距很小。
-
解题代码
-
func numTrees(n int)int{
dp := make([]int, n+1)
dp[0] = 1
for i := 1; i <= n; i++ {
for j := 1; j <= i; j++ {
dp[i] += dp[j-1] * dp[i-j]
}
}
return dp[n]
}
- 自己的代码(逻辑一样,只不过多讨论了初始值)
-
func numTrees(n int) int {
if n<3 {
return n
}
dp := make([]int, n + 1)
dp[0] = 1
dp[1] = 1
dp[2] = 2
for i:=3;i<=n;i++ {
for j:=1;j<=i;j++ {
dp[i] += dp[j-1] * dp[i-j]
}
}
return dp[n]
}
- 本题解题成功的主要原因是动规的五步方法论确实很方便解题,只要再辅助一些手写推导和对数据结构的理解,就有很大机会自己做出来。