代码随想录算法训练营day32 day33| 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯 62.不同路径 63. 不同路径 II
学习资料:https://programmercarl.com/动态规划理论基础.html#算法公开课
动态规划PART1
动规五部曲:
1,确定dp数组,i和dp[i]的含义
2,递推公式
3,dp数组初始化
4,确定遍历顺序
5,打印数组(用于debug)
常见题型:基本题目、背包问题、打家劫舍、股票问题、子序列问题
- 斐波那契数(前两个数的和为第三个数的值)
点击查看代码
class Solution(object):
def fib(self, n):
"""
:type n: int
:rtype: int
"""
# 排除corner case
if n==0:
return 0
# 1创建dp数组,从0到n有n+1个数
dp = [0]*(n+1)
# 3初始化dp数组
dp[0]=0
dp[1]=1
# 4遍历顺序:从前向后
for i in range(2, n+1):
# 2递归公式
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
70.爬楼梯(据题意,本阶楼梯可从前一阶跳一步或者前两阶跳两步到达,所以到本阶的方法是到前一阶和到前两阶的方法之和)
点击查看代码
class Solution(object):
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
if n<=1:
return n
dp = [0]*(n+1)
dp[1]=1
dp[2]=2
for i in range(3, n+1):
dp[i] = dp[i-1]+dp[i-2]
return dp[n]
746.使用最小花费爬楼梯(跳到第i阶的最小花费,对(到前一阶的最小花费+前一阶向上跳的花费,到前两阶的最小花费+前两阶向上跳的花费)取最小值)
点击查看代码
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
"""
动态规划,可以初始站在第0个台阶或第1个台阶,此时无花费,向上爬才消费cost
可以求出每个台阶对应的最小花费 dp[i]
在第i个台阶向上爬一格的花费=爬到第i个台阶的最小花费dp[i]+继续爬的cost[i]
"""
dp=[0]*(len(cost)+1)
dp[0]=0
dp[1]=0
for i in range(2, len(cost)+1): # 到楼顶是len(cost)
dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]) # 递归公式
return dp[len(cost)]
62.不同路径(到位置(i,j)的路径数 = 到(i-1,j)的路径数 + 到(i,j-1)的路径数;因为只能从它的上方或者左方走过来;对第一列和第一行初始化,因为他们这些位置的路线唯一)
点击查看代码
class Solution(object):
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
# 动态规划
# 构建二维数组为dp数组,dp[i][j]代表从起点走到[i,j]位置不同路径的个数
dp = [[0]*n for _ in range(m)]
# 初始化,第一行和第一列的上位置都只有一条直路,因为限制了只能向下或右走
for i in range(m):
dp[i][0] = 1
for j in range(n):
dp[0][j] = 1
# 遍历顺序:向下和向右
for i in range(1, m):
for j in range(1, n):
# 递归函数,只能从[i-1,j]和[i,j-1]两个位置到[i,j],那到[i,j]不同路径数就是从起点到前两点的路径数之和
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[m-1][n-1] # 返回终点的dp值
63.不同路径2(有障碍;起点或者终点有障碍,则死路一条;第一列或者第一行有障碍,则障碍及后面不用遍历,死路;中部有障碍,则跳出本次循环,继续向下遍历;到该点的路线种类=从起点到其左点的路线数+从起点到其上点的路线数)
点击查看代码
class Solution(object):
def uniquePathsWithObstacles(self, obstacleGrid):
"""
:type obstacleGrid: List[List[int]]
:rtype: int
"""
m = len(obstacleGrid)
n = len(obstacleGrid[0])
# 提前终止,当起点或者终点有障碍物
if obstacleGrid[0][0]==1 or obstacleGrid[-1][-1] == 1:
return 0
# 构建dp二维数组
dp = [[0]*n for _ in range(m)]
# dp数组的第一行和第一列初始化,当没有障碍时变为1,代表有一条路,若有障碍就break,因为后面的路都为0条没变
for i in range(m):
if obstacleGrid[i][0] == 0:
dp[i][0] = 1
else:
break
for j in range(n):
if obstacleGrid[0][j] == 0:
dp[0][j] = 1
else:
break
# 遍历顺序:向下向右!注意!因为公式i-1, j-1所以从第二行第二列开始遍历
for i in range(1,m):
for j in range(1,n):
# 递推前先判断有无障碍物,若有就直接跳到下一个循环
if obstacleGrid[i][j] == 1:
continue
dp[i][j] = dp[i-1][j]+dp[i][j-1] # 递推公式不变,还是其左其上两个位置的路径数之和
return dp[m-1][n-1]
PS:欠了三天的内容补上,背包问题明天补,脑子转不过来了,眼睛要闭上了。
动态规划有点难哦,多消化
这两天天气不错,最近面试挺多的,也发现了比较心怡的工作,有点兴奋又有点忐忑,希望能通过吧
不知道11月是不是我的丰收季节
吃了清汤黄牛肉味道还可以,终于吃了顿大餐
浙公网安备 33010602011771号