leetcode每日一题--矩阵中移动的最大次数

一.题目原型

二.思路解析

1.动态规划

这道题要求的是矩阵的最大移动次数。根据题目意思,从索引 0 列开始向右移动,每次移动一列,最多移动到 n - 1 列,也就是 n - 1次。其移动规则为:当前单元格可以移动到其右上方、正右方以及右下方三个位置中严格大于其的单元格。那么换个角度想,一个单元格可以从其左上方、正左方以及左下方的单元格转移过来。

这不就是我们动态规划最开始的典型思路吗?

在这题中,我们从第一列开始,往后能走多少,到了第几列,其实就是这一次的走的步数,走到最远的列,就是最大的移动次数。

我们创建一个二维dp数组,初始化为0,我们从不同行的第一列元素开始走,能走到的地方我们做一个标记为1,已经标记过的我们就不再去走了,以免死循环。

int m=grid.size(),n=grid[0].size(),max_j=0;
        vector<vector<int>>dp(m,vector<int>(n,0));
        for(int i=0;i<m;++i){//初始化
            dp[i][0]=1;
        }

dp【i】【j】由dp【i-1】【j-1】和dp【i】【j-1】和dp【i+1】【j】决定

for(int j=1;j<n;++j){//从第1列开始
            for(int i=0;i<m;++i){
                if((dp[i][j-1]==1&&grid[i][j]>grid[i][j-1])||(i-1>-1&&dp[i-1][j-1]==1&&grid[i][j]>grid[i-1][j-1])||(i+1<m&&dp[i+1][j-1]==1&&grid[i][j]>grid[i+1][j-1])){
                    dp[i][j]=1;
                    max_j=j;
                }
            }
        }

2.深度优先遍历

从第一列的任一单元格 (i,0)开始递归。枚举往右上/右/右下三个方向走,如果走一步后,没有出界,且格子值大于 grid[i][j],则可以走,继续递归。

在递归过程中,记录能访问到的最大列号,作为答案。

代码实现时,为避免重复递归之前访问过的格子,可以用一个 vis 数组标记访问过的格子。但实际上,可以把 grid[i][j]置为 0从而无需创建 vis数组。这是因为网格值均为正数,并且我们只能访问到比当前格子值更大的格子,所以置为 0 会导致永远无法访问到该格子,这正是我们所希望的。grid[i][j]置为0,表示这个点我们已经走过了,之后没有必要再走了,因为再往这个点走,移动的次数都是一样的。标记后就肯定不会再走了,因为要往后走的一个条件就是后面的点的值大于当前点的值。

function<void(int, int)> dfs = [&](int i, int j) {
            ans = max(ans, j);
            if (ans == n - 1) { // ans 已达到最大值
                return;
            }
            // 向右上/右/右下走一步
            for (int k = max(i - 1, 0); k < min(i + 2, m); k++) {
                if (grid[k][j + 1] > grid[i][j]) {
                    dfs(k, j + 1);
                }
            }
            grid[i][j] = 0;
        };

3.广度优先遍历

同样,我们也可以使用BFS的方法,我们首先将所有的行坐标加入集合中,表示这些地方都可以走,作为出发点。然后依次进行遍历,找到下一列可以走到的所有行坐标,这些下一列的行坐标都是严格满足比其走过来的点值要大的,然后将这些行坐标的集合替换掉之前的集合,再进行遍历,找到下一列能够走到的行坐标。直到没有能走的行坐标,或者走到最后一列为止。

我们需要一个vis数组去标记走过的位置,不能再走了

定义一个判断坐标合法的函数is_valid去判断坐标是不是合法的。

三.代码实现:

动态规划(DP)

class Solution {
public:
    int maxMoves(vector<vector<int>>& grid) {
        int m=grid.size(),n=grid[0].size(),max_j=0;
        vector<vector<int>>dp(m,vector<int>(n,0));
        for(int i=0;i<m;++i){//初始化
            dp[i][0]=1;
        }
        for(int j=1;j<n;++j){//从第1列开始
            for(int i=0;i<m;++i){
                if((dp[i][j-1]==1&&grid[i][j]>grid[i][j-1])||(i-1>-1&&dp[i-1][j-1]==1&&grid[i][j]>grid[i-1][j-1])||(i+1<m&&dp[i+1][j-1]==1&&grid[i][j]>grid[i+1][j-1])){
                    dp[i][j]=1;
                    max_j=j;
                }
            }
        }
        return max_j;
    }
};

深度优先遍历(DFS)

class Solution {
public:
    int maxMoves(vector<vector<int>> &grid) {
        int m = grid.size(), n = grid[0].size();
        int ans = 0;
        function<void(int, int)> dfs = [&](int i, int j) {
            ans = max(ans, j);
            if (ans == n - 1) { // ans 已达到最大值
                return;
            }
            // 向右上/右/右下走一步
            for (int k = max(i - 1, 0); k < min(i + 2, m); k++) {
                if (grid[k][j + 1] > grid[i][j]) {
                    dfs(k, j + 1);
                }
            }
            grid[i][j] = 0;
        };
        for (int i = 0; i < m; i++) {
            dfs(i, 0); // 从第一列的任一单元格出发
        }
        return ans;
    }
};

广度优先遍历(BFS)

class Solution {
public:
    int maxMoves(vector<vector<int>>& grid) {
        int M = grid.size(), N = grid[0].size();
        auto is_valid = [&](int r, int c) -> bool {
            return 0 <= r && r < M && 0 <= c && c < N;
        };
        queue<pair<int, int>> que;
        vector<vector<bool>> vis(M, vector<bool>(N, false));
        for (int r = 0; r < M; r++) {
            vis[r][0] = true;
            que.push(make_pair(r, 0));
        }
        int ans = 0;
        while (!que.empty()) {
            auto [r, c] = que.front(); que.pop();
            ans = max(ans, c);
            int ce = c+1;
            for (const int& re : {r-1, r, r+1}) {
                if (is_valid(re, ce) && !vis[re][ce] && grid[r][c] < grid[re][ce]) {
                    vis[re][ce] = true;
                    que.push(make_pair(re, ce));
                }
            }
        }
        return ans;
    }
};
相关推荐
终末圆5 分钟前
MyBatis XML映射文件编写【后端 18】
xml·java·开发语言·后端·算法·spring·mybatis
Damon小智7 分钟前
C#进阶-基于雪花算法的订单号设计与实现
开发语言·算法·c#·雪花算法·订单号
没什么技术27 分钟前
java实现LRU 缓存
java·算法·lru
我爱豆子33 分钟前
Leetcode Hot 100刷题记录 -Day19(回文链表)
java·算法·leetcode·链表
程序猿练习生40 分钟前
C++速通LeetCode中等第18题-删除链表的倒数第N个结点(最简单含注释)
c++·leetcode·链表
m0_571957581 小时前
Java | Leetcode Java题解之第424题替换后的最长重复字符
java·leetcode·题解
繁依Fanyi1 小时前
828 华为云征文|华为 Flexus 云服务器打造 Laverna 在线笔记应用
运维·服务器·笔记·python·算法·华为·华为云
little redcap1 小时前
KMP整理+个人推导+快速上手理解
数据结构·笔记·算法
蠢蠢的打码1 小时前
8587 行编辑程序
数据结构·c++·算法·链表
m0_617775391 小时前
代码随想录训练营第34天| 62.不同路径 、63. 不同路径 II
数据结构·算法·图论