[leedcode]刷题有感--动态规划入门及思路模板

简介: [leedcode]刷题有感--动态规划入门及思路模板

一、动态规划思考模板

1、构造dp[]数组,想清楚dp[]数组的具体含义。

2、确定本题目的递推公式

3、初始化dp[]数组

4、确定数组遍历顺序

5、利用初始化后的dp数组结合递推公式推导dp数组,看是否符合题意要求

二、题目示例

1、斐波那契数列-- 一维动态规划

斐波那契数列

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

示例 1:

  • 输入:2
  • 输出:1
  • 解释:F(2) = F(1) + F(0) = 1 + 0 = 1

示例 2:

  • 输入:3
  • 输出:2
  • 解释:F(3) = F(2) + F(1) = 1 + 1 = 2

示例 3:

  • 输入:4
  • 输出:3
  • 解释:F(4) = F(3) + F(2) = 2 + 1 = 3

提示:

  • 0 <= n <= 30

第一步、构造dp数组

vector<int>dp(n + 1);  //创建动态规划数组

明确dp数组的含义--dp[i]表示第i个数的大小


第二步、写出递推公式

dp[i] = dp[i - 1] + dp[i - 2];  //递推公式

通常根据题目中给出的信息进行模拟和找规律 ,最终找到递推公式


第三步、初始化dp数组

//初始化
dp[0] = 0;
dp[1] = 1;

根据题目需要进行数组初始化


第四步、确定数组的遍历顺序

for(int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];  //递推公式
        }

斐波那契数列--我们选择从前向后遍历


第五步、我们自己按照初始化与递推公式进行手动模拟 或 打印dp数组,检查正确与否


最后,呈上完整代码

class Solution {
public:
    int fib(int n) {
        if(n <= 1) return n;  //特判,否则for循环部位报错
        vector<int>dp(n + 1);  //创建动态规划数组
        dp[0] = 0;
        dp[1] = 1;  //初始化
        for(int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];  //递推公式
        }
        return dp[n];
    }
};

2、爬楼梯问题 -- 一维动态规划

爬楼梯问题

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

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

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

示例 1:

  • 输入: 2
  • 输出: 2
  • 解释: 有两种方法可以爬到楼顶。
  • 1 阶 + 1 阶
  • 2 阶

示例 2:

  • 输入: 3
  • 输出: 3
  • 解释: 有三种方法可以爬到楼顶。
  • 1 阶 + 1 阶 + 1 阶
  • 1 阶 + 2 阶
  • 2 阶 + 1 阶

爬楼梯问题属于经典的动态规划类问题,我们要从上文提到的动态规划五部曲来思考。


第一步、构造dp数组

vector<int>dp(n + 1);

我们首先构造一维dp数组


第二步、写出递推公式

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

我们首先发现本题的递推公式与上题斐波那契数列的递推公式一致,那么我们如何推导的呢?

假如n=3,即我们需要爬三层楼梯才能登上楼顶。

由于我们每次爬楼梯只能爬 一层 或 两层,所以肯定不可能从0层直接爬到三层。

如果想到达三层,只存在两种可能性--1、从一层处 爬两层楼梯到达三层,2、从二层处 爬一层楼梯到达三层。

但是我们不可能一开始就在 一层 或者 二层,我们的初始位置在零层,所以我们还要用同样的思路从零层 爬到 一层 或 二层。

故 dp[3] = dp[2] + dp[1]。 以此类推---dp[i] = dp[i - 1] + dp[i - 2]。


第三步、初始化dp数组

dp[1] = 1;
dp[2] = 2;

根据递推公式我们可以将dp数组初始化。


第四步、确定数组的遍历顺序

for(int i = 3; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }

因为我们必须 从最底层 一层层 向上爬楼梯,所以我们是从前向后遍历。


第五步、自己手动模拟或者打印dp数组进行检查正确与否。


最后、呈上本题完整代码

class Solution {
public:
    int climbStairs(int n) {
        if(n <= 1) return n;
        vector<int>dp(n + 1);
        dp[1] = 1;
        dp[2] = 2;
        for(int i = 3; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
};

3、不同路径问题 -- 二维动态规划

不同路径I

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

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

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

示例 1:

  • 输入:m = 3, n = 7
  • 输出:28

示例 2:

  • 输入:m = 2, n = 3
  • 输出:3

解释: 从左上角开始,总共有 3 条路径可以到达右下角。

  1. 向右 -> 向右 -> 向下
  2. 向右 -> 向下 -> 向右
  3. 向下 -> 向右 -> 向右

示例 3:

  • 输入:m = 7, n = 3
  • 输出:28

示例 4:

  • 输入:m = 3, n = 3
  • 输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 10^9

与爬楼梯问题不同的是,不同路径问题 是一个 图论中也经常出现的问题,在一个二维图表中,我们应使用二维动态规划。


第一步、构造dp数组,不过是二维数组。

vector<vector<int>>dp(m, vector<int>(n, 0));  //二维动归数组

m为行数, n为列数。dp[][]表示到达坐标(i,j)的位置共有多少种不同的走法。


第二部、写出递推公式。

dp[i][j] = dp[i - 1][j] + dp[i][j - 1];  //递推公式

题目中规定:我们只能向下走 或者 向右走。由此可得到递推公式。


第三步、初始化dp数组

for(int i = 0; i < n; i++) {
            dp[0][i] = 1;  //初始化行
        }
for(int i = 0; i < m; i++) {
            dp[i][0] = 1;  //初始化列
        }

第一行 和 第一列 上的所有位置 若想到达则只有1条路径可走,要么一直向右,要么一直向下走。


第四步、确定数组的遍历顺序

for(int i = 1; i < m; i++) {
            for(int j = 1; j < n; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];  //递推公式
            }
        }

遍历除 第一行 和 第一列 之外的每一个位置-->最终推出到达终点位置的路径数目总和。


第五步、同理打印dp或手动模拟检查正确与否。


相关文章
|
9月前
|
存储 索引
【冲击蓝桥篇】动态规划(下):你还在怕动态规划!?进来!答题模板+思路解析+真题实战
【冲击蓝桥篇】动态规划(下):你还在怕动态规划!?进来!答题模板+思路解析+真题实战
|
前端开发 JavaScript 数据库
刷题,巩固基础的好方法
刷题,巩固基础的好方法
114 0
刷题,巩固基础的好方法
|
存储 算法 Go
算法入门很简单:链表题套路及精选题目(上)
链表(Linked List):一种线性表数据结构。它使用一组任意的存储单元(可以是连续的,也可以是不连续的),来存储一组具有相同类型的数据。
算法入门很简单:链表题套路及精选题目(上)
|
算法 搜索推荐 C++
【算法】面试必备之0基础学算法 快速排序(详细讲解+私人笔记+代码展示)
二分查找又称折半查找、二分搜索、折半搜索等,是在分治算法基础上设计出来的查找算法,对应的时间复杂度为O(logn)。到这里是不是感觉很熟悉,我们前两期的算法知识,也是基于分治的方法去进行学习的,如果有这方面还不了解的朋友,你可以到我的第一篇文章(0基础学算法)里面去查看一下。
154 0
【算法】面试必备之0基础学算法 快速排序(详细讲解+私人笔记+代码展示)
|
人工智能 算法 搜索推荐
【算法】面试必备之0基础学算法 归并排序(详细讲解+私人笔记+代码展示)
归并排序是一个稳定的排序算法,在进行子数组合并的时候,我们可以设置当元素大小相等时,先将前半部分的数据放入临时数组,这样就可以保证相等元素在排序后依然保持原来的顺序,并且归并排序的执行效率与原始数据的有序程度无关,其时间复杂度是非常稳定的,都是O(nlogn) ...
158 0
【算法】面试必备之0基础学算法 归并排序(详细讲解+私人笔记+代码展示)
|
存储 算法
算法入门很简单:链表题套路及精选题目(下)
链表(Linked List):一种线性表数据结构。它使用一组任意的存储单元(可以是连续的,也可以是不连续的),来存储一组具有相同类型的数据。
|
算法
​LeetCode刷题实战390:消除游戏
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
259 0
​LeetCode刷题实战390:消除游戏
|
算法
​LeetCode刷题实战38: 外观数列
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
86 0
​LeetCode刷题实战38: 外观数列
|
算法
​LeetCode刷题实战36: 有效的数独
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
152 0
​LeetCode刷题实战36: 有效的数独
|
算法
​LeetCode刷题实战37: 解数独
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
138 0
​LeetCode刷题实战37: 解数独