来到动态规划的第三天了,题目会出现以前内容的融合,比如第二题会有BST
Given an integer?
n
, break it into the sum of?k
?positive integers, where?k >= 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 exactly?n
?nodes of unique values from?1
?to?n
.
当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]