来到动态规划的第三天了,题目会出现以前内容的融合,比如第二题会有BST
Given an integer
n
, break it into the sum ofk
positive integers, wherek >= 2
, and maximize the product of those integers.Return the maximum product you can get.
最少拆成两个数,最高没有上限,那怎么拆呢?
还得靠动态规划来解决,根据动态规划的分析过程来想:
1. 确定dp数组(dp table)以及下标的含义
dp[i]:分拆数字i,可以得到的最大乘积为dp[i]。
dp[i]的定义将贯彻整个解题过程,下面哪一步想不懂了,就想想dp[i]究竟表示的是啥!
2. 确定递推公式
可以想 dp[i]最大乘积是怎么得到的呢?
其实可以从1遍历j,然后有两种渠道得到dp[i].
一个是j * (i - j) 直接相乘。
一个是j * dp[i - j],相当于是拆分(i - j),对这个拆分不理解的话,可以回想dp数组的定义。
所以递推公式:dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j});
3. dp的初始化
这里只初始化dp[2] = 1,从dp[i]的定义来说,拆分数字2,得到的最大乘积是1,这个没有任何异议!
4. 确定遍历顺序
先来看看递归公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
dp[i] 是依靠 dp[i - j]的状态,所以遍历i一定是从前向后遍历,先有dp[i - j]再有dp[i]。
5. 举例推导dp数组
举例当n为10 的时候,dp数组里的数值,如下:
2 3 4 5 6 7 8 9 10
1 2 4 6 9 12 18 27 36
所以代码如下:
- class Solution:
- def integerBreak(self, n: int) -> int:
- dp = [0] * (n + 1)
- dp[2] = 1
-
- for i in range(3, n + 1):
-
- for j in range(1, i // 2 + 1):
-
- dp[i] = max(dp[i], (i - j) * j, dp[i - j] * j)
-
- return dp[n]
96. Unique Binary Search Trees
Given an integer
n
, return the number of structurally unique BST's (binary search trees) which has exactlyn
nodes of unique values from1
ton
.
当n为3的时候,可以通过n等于1和2的状态推导出来。
dp[3],就是 元素1为头结点搜索树的数量 + 元素2为头结点搜索树的数量 + 元素3为头结点搜索树的数量
元素1为头结点搜索树的数量 = 右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量
元素2为头结点搜索树的数量 = 右子树有1个元素的搜索树数量 * 左子树有1个元素的搜索树数量
元素3为头结点搜索树的数量 = 右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量
- class Solution:
- def numTrees(self, n: int) -> int:
- dp = [0] * (n + 1)
- dp[0] = 1
- for i in range(1, n + 1):
- for j in range(1, i + 1):
- dp[i] += dp[j - 1] * dp[i - j]
- return dp[n]