打卡Day33
- 1.62.不同路径
- 2.63. 不同路径 II
- 3.343. 整数拆分
- 4.96.不同的二叉搜索树
1.62.不同路径
题目链接:62.不同路径
文档讲解: 代码随想录
动规五部曲:
(1)确定dp数组和下标的含义
dp[ i ][ j ] 表示到达 i x j 网络的右下角有的路径数
(2)确定递推公式
机器人只能向下或者向右移动一步,那么就有dp[ i ][ j ] = dp[ i - 1 ][ j ] + dp[ i ][ j - 1]
(3)初始化
如果,m = 1 或者 n = 1,那么只有一种方法。当m,n 都大于等于2时,dp[ 0 ][ 1 ] = 1,dp[ 1 ][ 0 ] = 1
(4)遍历顺序:两层迭代,都从前往后
(5)打印数组
以上是我自己的思路,也不知道能不能做出来!初始化出现了问题,应该修改为除了dp [0][0](因为没有实际意义),dp[0] 和 dp[][0] 值都为1。遍历顺序再具体一点,先固定 i ,从前往后填满,然后令 i + 1,重复填满。运行出错,发现当起点和终点重合的时候(即 m = 1,n = 1),dp[0][0] = 1,那么就可以直接在新建列表的时候初始值为1。
class Solution(object):
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
#创建dp数组
dp = [[1] * n for _ in range(m)]
#初始化
#第一行
# for j in range(1, n):
# dp[0][j] = 1
# #第一列
# for i in range(1, m):
# dp[i][0] = 1
#填满
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i][j - 1] + dp[i - 1][j]
return dp[m - 1][n - 1]
2.63. 不同路径 II
题目链接:63. 不同路径 II
文档讲解: 代码随想录
动规五部曲:
(1)确定dp数组和下标
dp[ i ][ j ] 表示到达 i x j 网络的右下角有的路径数
(2)确定递归公式
dp[ i ][ j ] = dp[ i - 1 ][ j ] + dp[ i ][ j - 1],但要考虑若该点有障碍物,那么就保持初始状态
(3)初始化
新建的时候初始值为0,遍历第一行和第一列的时候,一旦某点是障碍,那么该点及之后的点都不可达,也就是保持初始化状态不变
(4)遍历顺序:两层迭代,先固定 i ,从前往后填满,然后令 i + 1,重复填满
(5)打印数组
class Solution(object):
def uniquePathsWithObstacles(self, obstacleGrid):
"""
:type obstacleGrid: List[List[int]]
:rtype: int
"""
m = len(obstacleGrid)
n = len(obstacleGrid[0])
#新建dp数组
dp = [[0] * n for _ in range(m)]
#初始化
for j in range(n):
if obstacleGrid[0][j] == 0:
dp[0][j] = 1
else:
break
for i in range(m):
if obstacleGrid[i][0] == 0:
dp[i][0] = 1
else:
break
#遍历
for i in range(1, m):
for j in range(1, n):
if obstacleGrid[i][j] == 0:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[m - 1][n - 1]
3.343. 整数拆分
题目链接:343. 整数拆分
文档讲解: 代码随想录
动规五部曲:
(1)确定dp数组和下标含义
dp[ i ]表示拆分数字 i,可以得到的最大乘积
(2)确定递推公式
从 1 遍历到 j,两种渠道得到dp[ i ],一个是 j 与 (i - j)相乘,另一种是 j 和 dp[i - j] 相乘。因为是从1遍历到 j,所以对 j 的拆分已经在遍历过程中实现了。因此递推公式为:dp[ i ] = max(dp[ i ], j * (i - j), j * dp[i - j])。为什么还要比较dp[i]呢?因为在递推公式推导的过程中,每次计算dp[i],取最大的而已。
(3)初始化
dp[0] dp[1] 就不应该初始化,也就是没有意义的数值。因为拆分0和1是无解的,因此初始化从2开始,dp[2] = 1。
(4)遍历顺序:从前往后遍历,需要两层循环,一层为 i,一层为 j。j的结束条件是 j < i - 1 ,其实 j < i 也是可以的,不过可以节省一步,例如让 j = i - 1,的话,其实在 j = 1的时候,这一步就已经拆出来了,重复计算,所以 j < i - 1。至于 i 是从3开始,这样dp[i - j]就是dp[2]正好可以通过我们初始化的数值求出来。可以进一步优化,拆分一个数n 使之乘积最大,那么一定是拆分成m个近似相同的子数相乘才是最大的。因此, j 遍历,只需要遍历到 n/2 就可以,后面就没有必要遍历了,一定不是最大值。
(5)打印数组
class Solution(object):
def integerBreak(self, n):
"""
:type n: int
:rtype: 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], j * (i - j), j * dp[i - j])
return dp[n]
4.96.不同的二叉搜索树
题目链接:96.不同的二叉搜索树
文档讲解: 代码随想录
又是完全没有思路。
作题的步骤:画个图举例看看。
然后就可以发现头节点为1时,右子树会有2个节点,和头节点为3时,左子树有2个节点,这两种情况,两棵树的布局是一样的。
动规五部曲:
(1)确定数组和下标
dp[ i ]表示节点数为 i 时, 互不相同的 二叉搜索树的种类
(2)递推关系式和遍历顺序
以3为例,可以分为头节点为1、2、3的三种情况。两层循环,一个是 i,一个是 j。当以 i 为节点总数时,以 j 为头节点,左子树有 j - 1个节点,右子树有 i - j 个节点。累加公式为:dp[ i ] += dp[ j ] * dp[i - j].
(3)初始化
dp[0] = 1
(4)打印数组
class Solution(object):
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
dp = [0] * (n + 1)
dp[0] = 1
for i in range(1, n + 1):
for j in range(0, i + 1):
dp[i] += dp[j - 1] * dp[i - j]
return dp[n]