leetcode 奇奇怪怪的BFS(529、417、1162、752、909)

529 扫雷游戏

题目描述: 给定一个代表游戏板的二维字符矩阵。 ‘M’ 代表一个未挖出的地雷,‘E’ 代表一个未挖出的空方块,‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字(‘1’ 到 ‘8’)表示有多少地雷与这块已挖出的方块相邻,‘X’ 则表示一个已挖出的地雷。

现在给出在所有未挖出的方块中(‘M’或者’E’)的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:

  1. 如果一个地雷(‘M’)被挖出,游戏就结束了- 把它改为 ‘X’。
  2. 如果一个没有相邻地雷的空方块(‘E’)被挖出,修改它为(‘B’),并且所有和其相邻的方块都应该被递归地揭露。
  3. 如果一个至少与一个地雷相邻的空方块(‘E’)被挖出,修改它为数字(‘1’到’8’),表示相邻地雷的数量。
  4. 如果在此次点击中,若无更多方块可被揭露,则返回面板。

思路: BFS。需要统计压入队列的每个位置周围八个方向是否有炸弹,如果存在炸弹,则该位置的值变为周围炸弹的数目,且该点将不再压入队列中继续BFS。只有周围炸弹为零时,该位置才压入队列,继续搜索。设置vis[][]数组,统计周围炸弹数目时,不需要考虑是否该点被遍历过(压入过队列),压入队列时,需考虑。

class Solution {
public:
    struct point{
        int x, y;
    };
    int dir[8][2] = {1,0,-1,0,0,1,0,-1,-1,-1,1,1,-1,1,1,-1};
    vector> updateBoard(vector>& board, vector& click) {
        int n = board.size(), m = board[0].size();
        if(board[click[0]][click[1]] == 'M') {
            board[click[0]][click[1]] = 'X';
            //cout<<"11111111"< Q;
        vector> vis(n, vector(m, 0));
        Q.push(point{click[0], click[1]});
        vis[click[0]][click[1]] = 1;
        while(!Q.empty()) {
            point q = Q.front();
            Q.pop();
            int cnt = 0;
            for(int i = 0; i < 8; i++) {
                int x = q.x + dir[i][0];
                int y = q.y + dir[i][1];
                if(x >= 0 && x < n && y >= 0 && y < m && board[x][y] == 'M')
                    cnt++;
            }
            if(cnt == 0) {
                board[q.x][q.y] = 'B';
                for(int i = 0; i < 8; i++) {
                    int x = q.x + dir[i][0];
                    int y = q.y + dir[i][1];
                    if(x >= 0 && x < n && y >= 0 && y < m && vis[x][y] == 0){
                        Q.push(point{x, y});
                        vis[x][y] = 1;
                    }
                       
                }
            }
            else
                board[q.x][q.y] = ('0' + cnt);
        }
        return board;
    }
};

417 太平洋大西洋水流问题

题目描述: 给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。
请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。
提示: 输出坐标的顺序不重要;m 和 n 都小于150。
思路: BFS逆向扩散。从两片海洋开始逆流,如果可以逆流到,就标记为1,然后检查两个海洋都可以逆流到的区域。

class Solution {
public:
    struct point {
        int x, y; 
    };
    int dir[4][2] = {1,0,-1,0,0,1,0,-1};
    vector> pacificAtlantic(vector>& matrix) {
        vector> ans;
        if(matrix.size() == 0 || matrix[0].size() == 0)
            return ans;
        int n = matrix.size(), m = matrix[0].size();
        queue Q1, Q2;
        vector> vis1(n, vector(m, 0));
        vector> vis2(n, vector(m, 0));
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(i == 0 || j == 0) {
                    Q1.push(point{i, j});
                    vis1[i][j] = 1;
                }  
                if(i == n - 1 || j == m - 1){
                    Q2.push(point{i, j});
                    vis2[i][j] = 1;
                }   
            }
        }
        while(!Q1.empty()) {
            point q = Q1.front();
            Q1.pop();
            for(int i = 0; i < 4; i++) {
                int x = q.x + dir[i][0];
                int y = q.y + dir[i][1];
                if(x >= 0 && x < n && y >= 0 && y < m && matrix[x][y] >= matrix[q.x][q.y] && vis1[x][y] == 0) {
                    Q1.push(point{x, y});
                    vis1[x][y] = 1;
                }
            }
        }
        while(!Q2.empty()) {
            point q = Q2.front();
            Q2.pop();
            for(int i = 0; i < 4; i++) {
                int x = q.x + dir[i][0];
                int y = q.y + dir[i][1];
                if(x >= 0 && x < n && y >= 0 && y < m && matrix[x][y] >= matrix[q.x][q.y] && vis2[x][y] == 0) {
                    Q2.push(point{x, y});
                    vis2[x][y] = 1;
                }
            }
        }
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(vis1[i][j] == 1 && vis2[i][j] == 1) {
                    vector tmp(2, 0);
                    tmp[0] = i;
                    tmp[1] = j;
                    ans.push_back(tmp);
                }
            }
        }
        return ans;
    }
};

1162 地图分析

题目描述: 你现在手里有一份大小为 N x N 的『地图』(网格) grid,上面的每个『区域』(单元格)都用 0 和 1 标记好了。其中 0 代表海洋,1 代表陆地,你知道距离陆地区域最远的海洋区域是是哪一个吗?请返回该海洋区域到离它最近的陆地区域的距离。
我们这里说的距离是『曼哈顿距离』( Manhattan Distance):(x0, y0) 和 (x1, y1) 这两个区域之间的距离是 |x0 - x1| + |y0 - y1| 。
如果我们的地图上只有陆地或者海洋,请返回 -1。
思路: BFS。用距离数组dis[][]表示所有点距离陆地的曼哈顿距离,则原本为陆地的点的曼哈顿距离为0,海洋的曼哈顿距离初始化为-1。从所有陆地出发,然后从陆地开始向外进行BFS式的扩散,然后每扩散一层就将计数器(即曼哈顿距离)加一,不断“填海造陆”直到整个地图再也不存在海洋为止,并且每更新一次计数器,同时更新最大的曼哈顿距离的值,最终返回最大值。

class Solution {
public:
    struct point {
        int x, y;
    };
    int dir[4][2] = {1,0,-1,0,0,1,0,-1};
    int maxDistance(vector>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector> dis(n, vector(m, -1));
        queue Q;
        int cnt = 0;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(grid[i][j] == 1) {
                    Q.push(point{i, j});
                    dis[i][j] = 0;
                    cnt++;
                }
            }
        }
        if(cnt == 0 || cnt == n * m)
            return -1;
        
        int ans = 0;
        while(!Q.empty()) {
            point q = Q.front();
            Q.pop();
            for(int i = 0; i < 4; i++) {
                int x = q.x + dir[i][0];
                int y = q.y + dir[i][1];
                if(x >= 0 && x < n && y >= 0 && y < m && grid[x][y] == 0 && dis[x][y] == -1) {
                    dis[x][y] = dis[q.x][q.y] + 1;
                    ans = max(ans, dis[x][y]);
                    Q.push(point{x, y});
                }
            }
        }
        return ans;
    }
};

752 打开转盘锁

题目描述: 你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,‘0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出最小的旋转次数,如果无论如何不能解锁,返回 -1。
思路: BFS。把问题转化成最短路径问题,这里的最短路径指的是由起始状态"0000"转换为目标状态所需要的最少变换次数,那么就可以用BFS来解决这一类最短路径问题。首先,先将deadends中的死亡数字存入到unordered_map中,将其值映射为1,方便之后查找得到的状态是否存在于deadends中。对于每一个状态,它可以扩展到最多 8 个状态,即将它的第 i = 0, 1, 2, 3 位增加 1 或减少 1,将这些状态中没有搜索过并且不在 deadends 中的状态全部加入到队列中,并继续进行搜索。

class Solution {
public:
    int openLock(vector& deadends, string target) {
        unordered_map mp;
        for(int i = 0; i < deadends.size(); i++) {
            mp[deadends[i]] = 1;
        }
        queue Q;
        unordered_map vis;
        unordered_map dis;
        if(mp["0000"] == 1)
            return -1;
        Q.push("0000");
        vis["0000"] = 1;
        while(!Q.empty()) {
            string q = Q.front();
            Q.pop();
            if(q == target)
                return dis[q];
            for(int i = 0; i < 4; i++) {
                string tmp1 = q, tmp2 = q;
                if(q[i] == '9') {
                    tmp1[i] = '0';
                    tmp2[i] = '8'; 
                }
                else if(q[i] == '0') {
                    tmp1[i] = '1';
                    tmp2[i] = '9';                    
                }
                else {
                    tmp1[i] += 1;
                    tmp2[i] -= 1;
                }
                if(mp[tmp1] == 0 && vis[tmp1] == 0) {
                    Q.push(tmp1);
                    vis[tmp1] = 1;
                    dis[tmp1] = dis[q] + 1;
                }
                if(mp[tmp2] == 0 && vis[tmp2] == 0) {
                    Q.push(tmp2);
                    vis[tmp2] = 1;
                    dis[tmp2] = dis[q] + 1;
                }
            }
        }
        return -1;
    }
};

909 蛇梯棋

题目描述: 在一块 N x N 的棋盘 board 上,从棋盘的左下角开始,每一行交替方向,按从 1 到 N * N的数字给方格编号。玩家从棋盘上的方格 1 (总是在最后一行、第一列)开始出发。每一次从方格 x 起始的移动都由以下部分组成:
你选择一个目标方块 S,它的编号是 x+1,x+2,x+3,x+4,x+5,或者 x+6,只要这个数字 <= N * N。
如果 S 有一个蛇或梯子,你就移动到那个蛇或梯子的目的地。否则,你会移动到 S。
在 r 行 c 列上的方格里有 “蛇” 或 “梯子”;如果 board[r][c] != -1,那个蛇或梯子的目的地将会是 board[r][c]。
注意,你每次移动最多只能爬过蛇或梯子一次:就算目的地是另一条蛇或梯子的起点,你也不会继续移动。
返回达到方格 N*N 所需的最少移动次数,如果不可能,则返回 -1。
思路: 将二维转换为一维,避免计算坐标,见change()函数。再进行BFS计算最少移动次数(即最小距离)。

class Solution {
public:
    void change(vector>& board, vector& , int N) {
        int num = 1, x = 0, y = N - 1;
        int dir = 1; // dir == 1为从左向右,dir== -1 为从右向左。
        while(num <= N * N) {
            [num] = board[y][x];
            num++;
            if(dir == 1 && x == N - 1) {
                dir = -1;
                y--;
            }
            else if(dir == -1 && x == 0){
                dir = 1;
                y--;
            }
            else
                x += dir;
        }
        return ;
    }
    int snakesAndLadders(vector>& board) {
        int N = board.size();
        vector dis(N * N + 1, -1);
        vector (N *N + 1, 0);
        change(board, , N);
        queue Q;
        Q.push(1);
        dis[1] = 0;
        while(!Q.empty()) {
            int q = Q.front();
            Q.pop();
            for(int i = 1; i <= 6; i++) {
                int pp = q + i;
                if(pp > N * N)
                    continue;
                if([pp] != -1) 
                    pp = [pp];
                if(dis[pp] != -1)
                    continue;
                dis[pp] = dis[q] + 1;
                Q.push(pp);
            }
        }
        return dis[N * N];
    }
};

你可能感兴趣的:(leetcode题目整理)