【算法】——动态规划题目讲解

简介: 【算法】——动态规划题目讲解

本期继续为大家带来的是关于动态规划类题目的讲解,对于这类题目大家一定要多加练习,争取掌握。

(一)不同路径

链接如下:62. 不同路径

  • 题目如下:

算法思路:

  • 1. 状态表⽰:

对于这种「路径类」的问题,我们的状态表⽰⼀般有两种形式:

  • i. 从 [i, j] 位置出发;
  • ii. 从起始位置出发,到达 [i, j] 位置。

这⾥选择第⼆种定义状态表⽰的⽅式:

  • dp[i][j] 表⽰:⾛到 [i, j] 位置处,⼀共有多少种⽅式。

  • 2. 状态转移⽅程:

简单分析⼀下。如果 dp[i][j] 表⽰到达 [i, j] 位置的⽅法数,那么到达 [i, j] 位置之 前的⼀⼩步,有两种情况:

  • i. 从 [i, j] 位置的上⽅( [i - 1, j] 的位置)向下⾛⼀步,转移到 [i, j] 位置;
  • ii. 从 [i, j] 位置的左⽅( [i, j - 1] 的位置)向右⾛⼀步,转移到 [i, j] 位置。

由于我们要求的是有多少种⽅法,因此状态转移⽅程就呼之欲出了:

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


  • 3. 初始化:

可以在最前⾯加上⼀个「辅助结点」,帮助我们初始化。使⽤这种技巧要注意两个点:

  • i. 辅助结点⾥⾯的值要「保证后续填表是正确的」;
  • ii. 「下标的映射关系」

在本题中,「添加⼀⾏」,并且「添加⼀列」后,只需将 dp[0][1] 的位置初始化为 1 即可。


  • 4. 填表顺序:

根据「状态转移⽅程」的推导来看,填表的顺序就是「从上往下」填每⼀⾏,在填写每⼀⾏的时候 「从左往右」


  • 5. 返回值:

根据「状态表⽰」,我们要返回 dp[m][n] 的值。


代码如下:

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> arr(m+1,vector<int>(n+1,0));
        arr[0][1] = 1;
        for(int i=1; i<= m; ++i){
            for(int j=1; j<= n; j++){
                arr[i][j] = arr[i-1][j] + arr[i][j-1];
            }
        }
         // 返回结果
        return arr[m][n];
    }
};

性能分析:

  • 时间复杂度:O(mn)。
  • 空间复杂度:O(mn)。

(二)不同路径||

链接如下:63. 不同路径 II

题目如下:

 

算法思路:

本题为不同路径的变型,只不过有些地⽅有「障碍物」,只要在「状态转移」上稍加修改就可解决。

  • 1. 状态表⽰:

对于这种「路径类」的问题,我们的状态表⽰⼀般有两种形式:

  • i. 从 [i, j] 位置出发;
  • ii. 从起始位置出发,到达 [i, j] 位置。

这⾥选择第⼆种定义状态表⽰的⽅式:

  • dp[i][j] 表⽰:⾛到 [i, j] 位置处,⼀共有多少种⽅式。

  • 2. 状态转移:

简单分析⼀下。如果 dp[i][j] 表⽰到达 [i, j] 位置的⽅法数,那么到达 [i, j] 位置之 前的⼀⼩步,有两种情况:

  • i. 从 [i, j] 位置的上⽅( [i - 1, j] 的位置)向下⾛⼀步,转移到 [i, j] 位置;
  • ii. 从 [i, j] 位置的左⽅( [i, j - 1] 的位置)向右⾛⼀步,转移到 [i, j] 位置。

但是, [i - 1, j] 与 [i, j - 1] 位置都是可能有障碍的,此时从上⾯或者左边是不可能 到达 [i, j] 位置的,也就是说,此时的⽅法数应该是 0。 由此我们可以得出⼀个结论,只要这个位置上「有障碍物」,那么我们就不需要计算这个位置上的值,直接让它等于 0 即可。


  • 3. 初始化:

可以在最前⾯加上⼀个「辅助结点」,帮助我们初始化。使⽤这种技巧要注意两个点:

  • i.    辅助结点⾥⾯的值要「保证后续填表是正确的」;
  • ii. 「下标的映射关系」

在本题中,添加⼀⾏,并且添加⼀列后,只需将 dp[1][0] 的位置初始化为 1 即可。


  • 4. 填表顺序:

根据「状态转移」的推导,填表的顺序就是「从上往下」填每⼀⾏,每⼀⾏「从左往右」。


  • 5. 返回值:

根据「状态表⽰」,我们要返回的结果是 dp[m][n] 。


代码如下:

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int len = obstacleGrid.size();
        int widelen=obstacleGrid[0].size();
        vector<vector<int>> arr(len+1,vector<int>(widelen+1,0));
        arr[0][1] = 1;
        for(int i=1; i<= len; ++i){
            for(int j=1; j<= widelen; ++j){
                if(obstacleGrid[i - 1][j - 1] == 0)
                    arr[i][j] = arr[i-1][j] + arr[i][j-1];
            }
        }
        return arr[len][widelen];
    }
};

性能分析:

  • 时间复杂度:O(mn)。
  • 空间复杂度:O(mn)。

 


(三)礼物的最⼤价值

链接如下:剑指 Offer 47. 礼物的最大价值

题目如下:

算法思路:

  • 1. 状态表⽰:

对于这种「路径类」的问题,我们的状态表⽰⼀般有两种形式:

  • i. 从 [i, j] 位置出发,巴拉巴拉;
  • ii. 从起始位置出发,到达 [i, j] 位置,巴拉巴拉。

这⾥选择第⼆种定义状态表⽰的⽅式:

  • dp[i][j] 表⽰:⾛到 [i, j] 位置处,此时的最⼤价值。

  • 2. 状态转移⽅程:

对于 dp[i][j] ,我们发现想要到达 [i, j] 位置,有两种⽅式:

  • i. 从 [i, j] 位置的上⽅ [i - 1, j] 位置,向下⾛⼀步,此时到达 [i, j] 位置能 拿到的礼物价值为 dp[i - 1][j] + grid[i][j] ;
  • ii. 从 [i, j] 位置的左边 [i, j - 1] 位置,向右⾛⼀步,此时到达 [i, j] 位置能 拿到的礼物价值为 dp[i][j - 1] + grid[i][j]

我们要的是最⼤值,因此状态转移⽅程为:

  • 💨   dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j] 。

  • 3. 初始化:

可以在最前⾯加上⼀个「辅助结点」,帮助我们初始化。使⽤这种技巧要注意两个点:

  • i. 辅助结点⾥⾯的值要「保证后续填表是正确的」;
  • ii. 「下标的映射关系」。

在本题中,「添加⼀⾏」,并且「添加⼀列」后,所有的值都为 0 即可。


  • 4. 填表顺序:

根据「状态转移⽅程」,填表的顺序是「从上往下填写每⼀⾏」,「每⼀⾏从左往右」。


  • 5. 返回值:

根据「状态表⽰」,我们应该返回 dp[m][n] 的值。


代码如下:

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int len = grid.size();
        int wide = grid[0].size();
        vector<vector<int>> dp(len + 1, vector<int>(wide + 1));
        for(int i = 1; i <= len; i++){
            for(int j = 1; j <= wide; j++){
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + grid[i - 1][j - 1];
            }
        }
        return dp[len][wide];
    }
};

性能分析:

  • 时间复杂度:O(mn)。
  • 空间复杂度:O(mn)。

以上便是本期动态规划的几道题目,大家做题时按照上述的“五步走”战略去分析思考,我相信大家都可以做对,

相关文章
|
29天前
|
算法
算法修炼-动态规划之路径问题(1)
算法修炼-动态规划之路径问题(1)
|
1月前
|
存储 算法 JavaScript
怎么刷算法,leetcode上有哪些经典题目
怎么刷算法,leetcode上有哪些经典题目
16 0
|
1月前
|
算法 测试技术 索引
动态规划算法练习题
动态规划算法练习题
23 0
|
1月前
|
存储 缓存 算法
【数据结构与算法】【小白也能学的数据结构与算法】递归 分治 迭代 动态规划 无从下手?一文通!!!
【数据结构与算法】【小白也能学的数据结构与算法】递归 分治 迭代 动态规划 无从下手?一文通!!!
|
8天前
|
算法
代码随想录算法训练营第五十六天 | LeetCode 647. 回文子串、516. 最长回文子序列、动态规划总结
代码随想录算法训练营第五十六天 | LeetCode 647. 回文子串、516. 最长回文子序列、动态规划总结
30 1
|
14天前
|
算法
算法系列--动态规划--背包问题(5)--二维费用背包问题(上)
算法系列--动态规划--背包问题(5)--二维费用背包问题(上)
19 0
|
14天前
|
算法
算法系列--动态规划--背包问题(4)--完全背包拓展题目(上)
算法系列--动态规划--背包问题(4)--完全背包拓展题目(上)
19 0
|
14天前
|
算法
算法系列--动态规划--背包问题(3)--完全背包介绍(下)
算法系列--动态规划--背包问题(3)--完全背包介绍(下)
16 0
|
14天前
|
存储 算法
算法系列--动态规划--⼦数组、⼦串系列(数组中连续的⼀段)(1)(下)
算法系列--动态规划--⼦数组、⼦串系列(数组中连续的⼀段)(1)
17 0
|
14天前
|
算法
算法系列--动态规划--⼦数组、⼦串系列(数组中连续的⼀段)(1)(上)
算法系列--动态规划--⼦数组、⼦串系列(数组中连续的⼀段)(1)
21 0