1391. Check if There is a Valid Path in a Grid

问题:

给定一个n*m数组,代表公路。

  • 1:连接 left + right
  • 2:连接 up + down
  • 3:连接 left + down
  • 4:连接 right + down
  • 5:连接 left + up
  • 6:连接 right + up

求是否能够从左上角格子(0,0)到达,右下角(n-1,m-1)。

Example 1:
Input: grid = [[2,4,3],[6,5,2]]
Output: true
Explanation: As shown you can start at cell (0, 0) and visit all the cells of the grid to reach (m - 1, n - 1).

Example 2:
Input: grid = [[1,2,1],[1,2,1]]
Output: false
Explanation: As shown you the street at cell (0, 0) is not connected with any street of any other cell and you will get stuck at cell (0, 0)

Example 3:
Input: grid = [[1,1,2]]
Output: false
Explanation: You will get stuck at cell (0, 1) and you cannot reach cell (0, 2).

Example 4:
Input: grid = [[1,1,1,1,1,1,3]]
Output: true

Example 5:
Input: grid = [[2],[2],[2],[2],[2],[2],[6]]
Output: true
 

Constraints:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
1 <= grid[i][j] <= 6

example 1:

example 2:

 

 

解法:BFS ,Union Find

解法一:BFS:

  • 状态:当前位置坐标。
    • visited也标记已访问过的坐标。
  • 选择:next=当前格子所连接的两个方向。
    • 除去:next:超出边界 or 已访问过位置 or 当前位置cur:不位于next的连接方向上。
      • op[cur].count(grid[next])==0

代码参考:

class Solution {
public:
    vector<vector<vector<int>>> dir = {{},{{0,-1},{0,1}},{{-1,0},{1,0}},{{0,-1},{1,0}},
                               {{1,0},{0,1}},{{0,-1},{-1,0}},{{-1,0},{0,1}}};
    vector<vector<unordered_set<int>>> opt = {{},{{1,4,6},{1,3,5}},{{2,3,4},{2,5,6}},
                                              {{1,4,6},{2,5,6}},{{2,5,6},{1,3,5}},
                                              {{1,4,6},{2,3,4}},{{2,3,4},{1,3,5}}};
    bool hasValidPath(vector<vector<int>>& grid) {
        int n = grid.size();
        int m = grid[0].size();
        queue<pair<int,int>> q;
        unordered_set<int> visited;//i*300+j
        q.push({0,0});
        visited.insert(0);
        while(!q.empty()) {
            auto[i,j] = q.front();
            q.pop();
            if(i==n-1 && j==m-1) return true;
            for(int k=0; k<2; k++) {//from & to
                auto d = dir[grid[i][j]][k];
                auto op = opt[grid[i][j]][k];
                int x = i+d[0];
                int y = j+d[1];
                //cout<<"x,y:"<<x<<","<<y<<endl;
                if(x<0 || y<0 || x>=n || y>=m || op.count(grid[x][y])==0) continue;
                if(visited.insert(x*300+y).second) {
                    q.push({x,y});
                    //cout<<"push:"<< grid[x][y]<<endl;
                }
            }
        }
        return false;
    }
};

解法二:Union Find

将每个cell的边+cell中心,都看作一个node,

那么每个街道的布局即可看作,将中心其中两个边 代表的node 相连。

例如:

3:连接 left + down

  • 假设中心为(i,j)
  • left边为(i,j-1)
  • down边为(i+1,j)

布局3则可看作,将上述三个节点相连。

 

根据题意,对于节点(i,j)

按照上述方法进行标记节点的话,其节点坐标为(i*2,j*2)

那么对于(0,0) 中心节点坐标为(0,0)上边(-1,0)左边(0,-1)

整个地图的上边界和左边界都为-1,而我们只要求中心节点:起始(0,0)~终点((n-1)*2, (m-1)*2),也用不到-1的节点,因此不计算在内也完全可以。

 

使用union find方法,

那么共有2n*2m个节点。

构造记录root数组 uf[n][m]

初始化为各自坐标自己。

实现find,merge方法:

class UnionFind {
public:
    vector<vector<int>> uf;
    int m, n;
    UnionFind(int m1, int n1) {
        m=m1, n=n1;
        uf.resize(2*n, vector<int>(2*m));
        for(int i=0; i<2*n; i++) {
            for(int j=0; j<2*m; j++) {
                uf[i][j] = i*600+j;
            }
        }
    }
    int find(int i, int j) {
        if(uf[i][j]!=(i*600+j)) {
            uf[i][j] = find(uf[i][j]/600, uf[i][j]%600);
        }
        return uf[i][j];
    }
    void merge(int i, int j, int x, int y) {
        if(x<0 || y<0 || x>=2*n || y>=2*m) return;
        int ij = find(i,j);//Root(i,j)
        int xy = find(x,y);//Root(x,y)
        if(ij==xy) return;
        uf[xy/600][xy%600] = ij;//connect two root:let uf(Root(x,y))==Root(i,j);
        //cout<<"merge:uf["<<x<<"]["<<y<<"]:"<<ij<<endl;
        return;
    }
};

再利用union find进行对本问题的求解。

对每个cell,根据道路布局,进行节点merge

最后所求即是,起始节点和终点是否为同一个root。

代码参考:

class Solution {
public:
    vector<vector<int>> dir = {{-1,0},{1,0},{0,-1},{0,1}};//edges from center: up,down,left,right
    vector<unordered_set<int>> opt = {{2,5,6},{2,3,4},{1,3,5},{1,4,6}};
    bool hasValidPath(vector<vector<int>>& grid) {
        int n = grid.size();
        int m = grid[0].size();
        UnionFind node(m,n);
        for(int i=0; i<n; i++) {
            for(int j=0; j<m; j++) {
                for(int k=0; k<4; k++) {
                    if(opt[k].count(grid[i][j]))
                        //connect: center + edge
                        //center: 2*i, 2*j
                        node.merge(2*i,2*j, 2*i+dir[k][0], 2*j+dir[k][1]); 
                }
            }
        }
        return node.find(0,0) == node.find(2*n-2, 2*m-2);
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值