leetcode 二维矩阵BFS(130、542、200、934、864(hard))

130 被围绕的区域

题目描述: 给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。
解释: 被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
思路: 将矩阵外面一圈为‘O’的位置存到队列中,BFS,找到所有从最外层‘O’能到达的‘O’,记录下来,则除了这些位置,其他位置都将为‘X’。

class Solution {
public:
	struct point
	{
		int x, y;
	};

	int dir[4][2] = {0,1,0,-1,1,0,-1,0};

    void solve(vector<vector<char>>& board) {
        if(board.size() == 0)
            return;
    	int n = board.size(), m = board[0].size();
    	queue<point> Q;
    	vector<vector<int>> vis(n, vector<int>(m, 0));
    	// 将矩阵外面一圈为‘O’的位置存到队列中
    	for(int i = 0; i < m; i++) {
    		if(board[0][i] == 'O') {
    			Q.push(point{0, i});
    			vis[0][i] = 1;
    		}
    		if(board[n - 1][i] == 'O'){
    			Q.push(point{n - 1, i});
    			vis[n - 1][i] = 1;
    		}
    	}    
    	for(int i = 0; i < n; i++) {
    		if(board[i][0] == 'O'){
    			vis[i][0] = 1;
    			Q.push(point{i, 0});
    		}
    		if(board[i][m - 1] == 'O'){
    			vis[i][m - 1] = 1;
    			Q.push(point{i, m - 1});
    		}
    	}

    	while(!Q.empty()) {
    		point u = Q.front();
    		Q.pop();
    		for(int i = 0; i < 4; i++) {
    			int x1 = u.x + dir[i][0];
    			int y1 = u.y + dir[i][1];
    			if(x1 >= 0 && x1 < n && y1 >= 0 && y1 < m && board[x1][y1] == 'O' && vis[x1][y1] == 0) {
    				vis[x1][y1] = 1;
    				Q.push(point{x1, y1});
    			}
    		}
    	}

    	for(int i = 0; i < vis.size(); i++) {
    		for(int j = 0; j < vis[0].size(); j++) {
    			if(board[i][j] == 'O' && vis[i][j] == 0)
    				board[i][j] = 'X';
    		}
    	}
    	return ;
    }
};

542 '01’矩阵

题目描述: 给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。两个相邻元素间的距离为 1 。
解释: 给定矩阵的元素个数不超过 10000;给定矩阵中至少有一个元素是 0;矩阵中的元素只在四个方向上相邻: 上、下、左、右。
思路: 逆向BFS。 从所有0同时出发,将遍历到的位置标上层数。则每个位置的层数就是其到最近的0的距离(因为最近的0会最先遍历到该位置,之后将该位置的标志数组记为1,不再遍历)。

class Solution {
public:
    // 逆向bfs 从所有0同时出发,将遍历到的位置标上层数
    struct point {
        int x, y;
    };

    int dir[4][2] = {1,0,-1,0,0,1,0,-1};

    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        int n = matrix.size(), m = matrix[0].size();
        vector<vector<int>> dis(n, vector<int>(m, -1));
        queue<point> Q;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(matrix[i][j] == 0) {
                    Q.push(point{i, j});
                    dis[i][j] = 0;
                }
            }
        }
        while(!Q.empty()) {
            point u = Q.front();
            Q.pop();
            for(int i = 0; i < 4; i++) {
                int x = u.x + dir[i][0];
                int y = u.y + dir[i][1];
                if(x >= 0 && x < n && y >= 0 && y < m && matrix[x][y] == 1 && dis[x][y] == -1) {
                    Q.push(point{x, y});
                    dis[x][y] = dis[u.x][u.y] + 1;
                }
            }
        }
        return dis;
    }
};

200 岛屿数量

题目描述: 给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。
思路: 遍历整个二维数组,将值为‘1’且没有被遍历过的位置压入一个新的队列,且将岛屿数量加一。由该位置开始,BFS,将所有遍历到的位置的标志数组置为1。遍历完整个二维数组,则求出岛屿数量。

class Solution {
public:
	struct point
	{
		int x,y;
	};

	int dir[4][2] = {1,0,-1,0,0,1,0,-1};

	void bfs(int st_x, int st_y, vector<vector<char>>& grid, vector<vector<int>>& vis, int n, int m) {
		queue<point> Q;
		Q.push(point{st_x, st_y});
		vis[st_x][st_y] = 1;
		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] == '1' && vis[x][y] == 0) {
					Q.push(point{x, y});
					vis[x][y] = 1;
				}
			}
		}
		return ;
	}
    int numIslands(vector<vector<char>>& grid) {
    	if(grid.size() == 0 || grid[0].size() == 0)
    		return 0;
        int n = grid.size(), m = grid[0].size();
        vector<vector<int>> vis(n, vector<int>(m, 0));
        int ans = 0;
        for(int i = 0; i < n; i++) {
        	for(int j = 0; j < m; j++) {
        		if(grid[i][j] == '1' && vis[i][j] == 0) {
        			ans ++;
        			bfs(i, j, grid, vis, n, m);
        		}
        	}
        }
        return ans;
    }
};

934 最短的桥

题目描述: 在给定的二维二进制数组 A 中,存在两座岛。(岛是由四面相连的 1 形成的一个最大组。)现在,我们可以将 0 变为 1,以使两座岛连接起来,变成一座岛。返回必须翻转的 0 的最小数目。(可以保证答案至少是 1。)
注意: 1 <= A.length = A[0].length <= 100;A[i][j] == 0 或 A[i][j] == 1
思路: 二维数组中一共存在两座不相连的岛。声明两个队列,先应用BFS将其中任意一座岛的位置的标志数组均置为1,使其中一个队列中仍压入第一个岛的所有位置。从第一个岛的所有位置出发,再次BFS,直到遇到另一个岛中的任意一个位置(即遇到值为‘1’,且标志数组为0的位置)。返回该点的距离数组的值即为答案。

class Solution {
public:
    struct point {
        int x, y;
    };

    int dir[4][2] = {1,0,-1,0,0,1,0,-1};

    int shortestBridge(vector<vector<int>>& A) {
        
        int n = A.size(), m = A[0].size();
        vector<vector<int>> vis(n, vector<int>(m, 0));
        queue<point> Q;
        queue<point> Q1;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(A[i][j] == 1) {
                    Q.push(point{i, j});
                    Q1.push(point{i, j});
                    vis[i][j] = 1;
                    break;
                }
            }
            if(!Q.empty())
                break;
        }
        vector<vector<int>> dis(n, vector<int>(m, 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 && A[x][y] == 1 && vis[x][y] == 0) {
                    Q.push(point{x, y});
                    Q1.push(point{x, y});
                    vis[x][y] = 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 && A[x][y] == 0 && vis[x][y] == 0) {
                    Q1.push(point{x, y});
                    vis[x][y] = 1;
                    dis[x][y] = dis[q.x][q.y] + 1;
                }
                else if(x >= 0 && x < n && y >= 0 && y < m && A[x][y] == 1 && vis[x][y] == 0)
                    return dis[q.x][q.y];
            }
        }
        return 0;
    }
};

864 获取所有钥匙的最短路径

题目描述: 给定一个二维网格 grid。 “.” 代表一个空房间, “#” 代表一堵墙, “@” 是起点,(“a”, “b”, …)代表钥匙,(“A”, “B”, …)代表锁。
我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
假设 K 为钥匙/锁的个数,且满足 1 <= K <= 6,字母表中的前 K 个字母在网格中都有自己对应的一个小写和一个大写字母。换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。
返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。
思路: BFS + 状态压缩。在point中添加value属性,用来记录获取了多少把钥匙。记录方法为二进制位,相应位置获得了钥匙,则值为一,否则值为零。因为钥匙数不超过6,所以只需要6个二进制位就可以代表所有可能的手里钥匙的状态,用一个三维数组dis[i][j][k]代表走到第i行j列手里拥有的钥匙状态为k时需要的步数,剩下的就是简单的bfs。

class Solution {
public:
    struct point {
        int x, y, state;
    };
    int dir[4][2] = {1,0,-1,0,0,1,0,-1};

    bool out(int x, int y, int n, int m) {
        if(x >= 0 && x < n && y >= 0 && y < m)
            return false;
        return true;
    }

    int shortestPathAllKeys(vector<string>& grid) {
        int n = grid.size(), m = grid[0].size();
        int k = 0;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(grid[i][j] >= 'a' && grid[i][j] <= 'z')
                    k++;
            }
        }
        int pow2_k = 1;
        for(int i = 1; i <= k; i++) {
            pow2_k *= 2;
        }
        vector<vector<vector<int>>> dis(n, vector<vector<int>>(m, vector<int>(pow2_k, -1)));
        queue<point> Q;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(grid[i][j] == '@') {
                    Q.push(point{i, j, 0});
                    dis[i][j][0] = 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];
                int state = q.state;
                if(out(x, y, n, m) || grid[x][y] == '#')
                    continue;
                if(grid[x][y] >= 'a' && grid[x][y] <= 'z')
                    state |= (1 << (grid[x][y] - 'a'));
                if(grid[x][y] >= 'A' && grid[x][y] <= 'Z' && ((state & (1 << grid[x][y] - 'A')) == 0))
                    continue;
                if(dis[x][y][state] == -1) {
                    Q.push(point{x, y, state});
                    dis[x][y][state] = dis[q.x][q.y][q.state] + 1;
                    if(state == pow2_k - 1)
                        return dis[x][y][state];
                }
            }
        }
        return -1;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值