1. 程式人生 > 其它 >[二分廣搜或倒序並查集]5845. 你能穿過矩陣的最後一天

[二分廣搜或倒序並查集]5845. 你能穿過矩陣的最後一天

力扣 5845. 你能穿過矩陣的最後一天
https://leetcode-cn.com/problems/last-day-where-you-can-still-cross/

解析:
二分廣搜
因為以最後一天可聯通為分界線,前面的日子都是可以從第一行到最後一行的,後面的不可以,所以可以二分以第幾天為分界線。
複雜度 \(O(n^2logn)\)

class Solution {
public:
    int latestDayToCross(int row, int col, vector<vector<int>>& cells) {
        int l = 1,r = row * col;
        while(l < r){
            int mid = (l + r + 1) >> 1;
            if(ck(mid, row, col, cells)) l = mid;
            else r = mid - 1;
        }
        return l;
    }

    bool ck(int n,int row,int col,vector<vector<int>>& cells){
        vector<vector<int>> g(row,vector<int>(col,1));
        int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
        for(int i=0;i<n;i++){
            int x = cells[i][0],y = cells[i][1];
            g[x-1][y-1] = 0;
        }
        queue<pair<int,int>> q;
        for(int i=0;i<col;i++){
            if(g[0][i]){
                q.push({0,i});
                g[0][i] = 0;
            }
        }
        while(q.size()){
            auto t = q.front();q.pop();
            int x = t.first,y = t.second;
            if(x == row - 1) return true;

            for(int i=0;i<4;i++){
                int nx = x + dx[i];
                int ny = y + dy[i];
                if(nx < 0 || nx >= row || ny < 0 || ny >= col) continue;
                if(g[nx][ny] == 0) continue;
                g[nx][ny] = 0;
                q.push({nx,ny});
            }
        }
        return false;
    }
};

從後往前推+並查集
從後往前,如果如果當前結點四個方向中某個方向在集合裡了,就將他們連線起來
定義兩個超級結點,第一個算作連線第一行的所有結點,第二個算作連線最後一行的所有結點。
如果當前結點在第一行就與第一個超級結點合併,
如果當前結點在最後一行就與第二個超級結點合併。

如果第一個超級結點與第二個超級結點時聯通的說明當前有第一行與最後一行之間有通路。

class Solution {
public:
    int latestDayToCross(int row, int col, vector<vector<int>>& cells) {
        vector<int> f;
        for(int i=0;i<row*col+2;i++){
            f.push_back(i);
        }
        int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
        int cnt = 0;
        int n = cells.size(),sz = row * col;
        vector<vector<int>> vis(row,vector<int>(col,0));
        for(int i=n-1;i>=0;i--){
            int x = cells[i][0] - 1,y = cells[i][1] - 1;
            int id = x * col + y;
            vis[x][y] = 1;
            for(int i=0;i<4;i++){
                int nx = x + dx[i];
                int ny = y + dy[i];
                if(nx < 0 || nx >= row || ny < 0 || ny >= col || !vis[nx][ny]) continue;
                int nid = nx * col + ny;
                unions(id,nid,f);
            }
            if(x == 0) unions(id,row*col,f);
            if(x == row - 1) unions(id,row*col+1,f);
            if(finds(sz,f) == finds(sz+1,f)){
                cnt++;
                break;
            }else{
                cnt++;
            }
        }
        return n - cnt;
    }

    int finds(int x,vector<int>& f){
        return x == f[x] ? x : f[x] = finds(f[x],f);
    }

    bool unions(int x,int y,vector<int>& f){
        x = finds(x,f);
        y = finds(y,f);
        if(x != y){
            f[x] = y;
            return false;
        }
        return true;
    }
    
};