代码随想录算法训练营day32 day33| 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯 62.不同路径 63. 不同路径 II

学习资料:https://programmercarl.com/动态规划理论基础.html#算法公开课

动态规划PART1
动规五部曲:
1,确定dp数组,i和dp[i]的含义
2,递推公式
3,dp数组初始化
4,确定遍历顺序
5,打印数组(用于debug)

常见题型:基本题目、背包问题、打家劫舍、股票问题、子序列问题

  1. 斐波那契数(前两个数的和为第三个数的值)
点击查看代码
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月是不是我的丰收季节
吃了清汤黄牛肉味道还可以,终于吃了顿大餐

posted @ 2024-11-03 21:19  Tristan241001  阅读(28)  评论(0)    收藏  举报