动态规划的小总结(一)

前言

这篇文章展示了规范化的动态规划做题步骤。部分内容借鉴了代码随想录代码随想录-动态规划

509. 斐波那契数

题目描述和思路

力扣题目链接(opens new window)

斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1 给你n ,请计算 F(n) 。

典型的动态规划题目。

  1. 确定dp数组以及下标的含义

    dp[i]:第i个数的斐波那契数值

  2. 递推公式

    dp[i] = dp[i - 1] + dp[i - 2]

  3. dp数组初始化

    dp[0] = 0, dp[1] = 1

  4. 遍历顺序

    从前往后

  5. 简单推导dp数组,以便后面debug时验证

    0 1 1 2 3 5 8 13 21 34 55

代码

class Solution {
public:
    int dp[40];
    
    int fib(int n) {
        dp[0] = 0, dp[1] = 1;
        for(int i = 2; i < 40; i++){
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
};

70. 爬楼梯

力扣题目链接(opens new window)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

和斐波那契数列有些相似。通过分析步骤看看不同的地方。

  1. 确定dp数组以及下标的含义

    dp[i]:有多少种不同方法到达第i个台阶

  2. 递推公式

    dp[i] = dp[i - 1] + dp[i - 2]

    第i个台阶,可以由第i - 1个台阶爬一个 和 第i - 2个台阶爬两个到达,

  3. dp数组初始化

    dp[1] = 1, dp[2] = 2

  4. 遍历顺序

    从前往后

  5. 简单推导dp数组,以便后面debug时验证

    0 1 2 3 5 8 13 21 34

所以其实很明显看到和斐波那契数列的区别主要在dp数组的含义,因为dp数组含义不同了,dp数组初始化方式不同。

代码

class Solution {
public:
    long long dp[50] = {0};
    int climbStairs(int n) {
        dp[1] = 1, dp[2] = 2;
        for(int i = 3; i < 50; i++){
            dp[i] = dp[i - 1] + dp[i - 2];
        }

        return dp[n];
    }
};

746. 使用最小花费爬楼梯

题目描述和思路

力扣题目链接(opens new window)

旧题目描述

数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。

每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。

请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。

  1. 确定dp数组以及下标的含义

    dp[i]:到达第i个阶梯的时候,使用的最小花费

  2. 递推公式

    dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]

    第i个阶梯,可以由第i - 1个台阶爬一个 和 第i - 2个台阶爬两个到达,取两者之间的花费最少。

  3. dp数组初始化

    dp[0] = 0, dp[1] = 0

  4. 遍历顺序

    从前往后

  5. 简单推导dp数组,以便后面debug时验证

    这个要看具体cost数组

代码

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        long long dp[1010] = {0};
        dp[0] = 0, dp[1] = 0;
        for(int i = 2; i <= cost.size(); i++){
            dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
        }
        return dp[cost.size()];
    }
};

62.不同路径

力扣题目链接(opens new window)

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

  1. 确定dp数组以及下标的含义

    dp[i][j]:到达[i, j]有多少条不同的路径

  2. 递推公式

    dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

    [i, j]可以由[i - 1, j]向下移动一步和[i, j - 1]向右移动一步到达。

  3. dp数组初始化

    for(int i = 1; i <= m; i++)dp[i][1] = 1;
    for(int i = 1; i <= n; i++)dp[1][i] = 1;
    

    最左边不能由左边右移一步得到,所以路径只有一条。最上面同理。

  4. 遍历顺序

    从左往右,从上到下

  5. 简单推导dp数组,以便后面debug时验证

    dp[2][2] = 2
    dp[2][3] = 3
    dp[2][4] = 4
    dp[2][5] = 5
    dp[2][6] = 6
    dp[2][7] = 7
    dp[3][2] = 3
    dp[3][3] = 6
    

代码

class Solution {
public:
    int dp[110][110];
    int uniquePaths(int m, int n) {
        for(int i = 1; i <= m; i++)dp[i][1] = 1;
        for(int i = 1; i <= n; i++)dp[1][i] = 1;
        for(int i = 2; i <= m; i++)
            for(int j = 2; j <= n; j++){
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                printf("dp[%d][%d] = %d\n", i, j, dp[i][j]);
            }
        return dp[m][n];
    }
};

63. 不同路径 II

题目描述和思路

力扣题目链接(opens new window)

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
动态规划的小总结(一)_第1张图片
网格中的障碍物和空位置分别用 1 和 0 来表示。

  1. 确定dp数组以及下标的含义

    dp[i][j]:到达[i, j]有多少条不同的路径,同时dp[i][j] = 0表示这是障碍物,不能到达。

  2. 递推公式

    dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

    [i, j]可以由[i - 1, j]向下移动一步和[i, j - 1]向右移动一步到达。

  3. dp数组初始化

    for(int i = 0; i < m && !obstacleGrid[i][0]; i++)dp[i][0] = 1;
    for(int i = 0; i < n && !obstacleGrid[0][i]; i++)dp[0][i] = 1;
    

    最左边不能由左边右移一步得到,所以路径只有一条。最上面同理。并且由于只有一条路,所以遇到障碍,后面的都不能到达。

  4. 遍历顺序

    从左往右,从上到下

  5. 简单推导dp数组,以便后面debug时验证

代码

class Solution {
public:
    int dp[110][110];
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        if(obstacleGrid[0][0] == 1)return 0;

        for(int i = 0; i < m && !obstacleGrid[i][0]; i++)dp[i][0] = 1;
        for(int i = 0; i < n && !obstacleGrid[0][i]; i++)dp[0][i] = 1;

        for(int i = 1; i < m; i++)
            for(int j = 1; j < n; j++){
                if(!obstacleGrid[i][j]){
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                    
                }else {
                    dp[i][j] = 0;
                }
            }
        
        // for(int i = 0; i < m; i++)
        //     for(int j = 0; j < n; j++)
        //         printf("dp[%d][%d] = %d\n", i, j, dp[i][j]);
        
        return dp[m - 1][n - 1];

    }
};

你可能感兴趣的:(动态规划,动态规划,算法)