leetcode1129. 顏色交替的最短路徑/單源最短路徑,BFS,DFS

標題:leetcode1129. 顏色交替的最短路徑

在一個有向圖中,節點分別標記爲 0, 1, …, n-1。這個圖中的每條邊不是紅色就是藍色,且存在自環或平行邊。

red_edges 中的每一個 [i, j] 對錶示從節點 i 到節點 j 的紅色有向邊。類似地,blue_edges 中的每一個 [i, j] 對錶示從節點 i 到節點 j 的藍色有向邊。

返回長度爲 n 的數組 answer,其中 answer[X] 是從節點 0 到節點 X 的最短路徑的長度,且路徑上紅色邊和藍色邊交替出現。如果不存在這樣的路徑,那麼 answer[x] = -1。

示例 1:

輸入:n = 3, red_edges = [[0,1],[1,2]], blue_edges = []
輸出:[0,1,-1]

示例 2:

輸入:n = 3, red_edges = [[0,1]], blue_edges = [[2,1]]
輸出:[0,1,-1]

示例 3:

輸入:n = 3, red_edges = [[1,0]], blue_edges = [[2,1]]
輸出:[0,-1,-1]

示例 4:

輸入:n = 3, red_edges = [[0,1]], blue_edges = [[1,2]]
輸出:[0,1,2]

示例 5:

輸入:n = 3, red_edges = [[0,1],[0,2]], blue_edges = [[1,0]]
輸出:[0,1,1]

提示:

  • 1 <= n <= 100
  • red_edges.length <= 400
  • blue_edges.length <= 400
  • red_edges[i].length == blue_edges[i].length == 2
  • 0 <= red_edges[i][j], blue_edges[i][j] < n

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/shortest-path-with-alternating-colors
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

基本思想1:BFS

  • 用二維矩陣保存圖的結構
    題目中說兩個節點之間存在平行邊,意思是說兩個節點之間同時有紅邊和藍邊,爲了便於處理,將邊的顏色當作邊的權重,紅色 :1,藍色:-1,紅藍兩色:0
  • bfs的過程:
    隊列中保存當前的節點,以及下一步要走的邊的顏色(-1,0,1)
    如果該邊已經走過,標記爲不可達,保證一條邊只能走一次,因爲題目中說存在自環;
    當兩個節點間存在兩條邊時,要確定目前走的是哪條邊,還剩哪條邊.
class Solution {
public:
    vector<int> shortestAlternatingPaths(int n, vector<vector<int>>& red_edges, vector<vector<int>>& blue_edges) {
        vector<vector<int>> edges(n, vector<int>(n, 0x3f3f3f3f));
        for(auto r : red_edges){
            edges[r[0]][r[1]] = 1;//紅邊用1表示
        }
        for(auto b : blue_edges){
            if(edges[b[0]][b[1]] == 1)
                edges[b[0]][b[1]] = 0;//紅藍用0表示
            else
                edges[b[0]][b[1]] = -1;//藍邊用-1表示
        }
        
        vector<int> dis(n, INT_MAX);
        
        bfs(edges, dis, n, -1);//-1:表示當前紅藍邊都可以走的時候,走紅邊
        bfs(edges, dis, n, 1);//1:表示當前紅藍邊都可以走的時候,走藍邊
        for(auto &d : dis){            
            if(d == INT_MAX)
                d = -1;
        }
        return dis;
    }
private:
    void bfs(vector<vector<int>> edges, vector<int> &dis, int n, int k){
        queue<pair<int, int>> q;//第一個數字表示當前的節點,第二個數字表示想要走的邊
        q.push({0, 0});//初始時,從0開始,走紅邊藍邊都可以
        dis[0] = 0;
        int cur = 0;//記錄路徑的長度,相當於當前遍歷的層數
        while(!q.empty()){
            ++cur;
            int sn = q.size();
            while(sn--){//遍歷該層節點
                int point = q.front().first;
                int flag = q.front().second;
                q.pop();
                for(int i = 0; i < n; ++i){
                    int cflag = flag;
                    if(edges[point][i] != 0x3f3f3f3f){//該條邊能走

                        if(flag == 0){//走紅邊,藍邊都可以
                            if(edges[point][i] == 1){//走紅邊
                                cflag = -1;//下一次走藍邊
                                edges[point][i] = 0x3f3f3f3f;
                            }
                            else if(edges[point][i] == -1){
                                cflag = 1;
                                edges[point][i] = 0x3f3f3f3f;
                            }
                            else{//有兩條邊,選擇紅邊
                                cflag = k;
                                edges[point][i] = k;
                            }
                            q.push({i, cflag});
                            dis[i] = min(dis[i], cur);
                        }
                        else{//只能走紅邊或者藍邊
                            if(flag == edges[point][i]){
                                edges[point][i] = 0x3f3f3f3f;
                                q.push({i, -flag});
                                dis[i] = min(dis[i], cur);
                            }
                            else if(edges[point][i] == 0){
                                edges[point][i] = -flag;
                                q.push({i, -flag});
                                dis[i] = min(dis[i], cur);
                            }
                        }
                    }
                }   
            }        
        }
    }
};

基本思想2:dfs

參考:leetcode題解

  • 用二維矩陣保存圖
    和解法1稍微有些變化,紅邊:1,藍邊:0,紅藍:-1。便於更新路徑長度數組
  • 維護一個二維數組:
    分別表示通過紅邊到達該節點的路徑長度,以及通過藍邊到達該節點的長度。目的是便於更新其他的節點的路徑長度
  • dfs過程:(特別注意下:dfs的條件判斷)
    需要指示當前要走紅邊還是藍邊
    更新路徑:要根據上一步所走的邊的顏色來更新當前路徑,例如:這一次要走紅邊,那麼要根據上一次走的藍邊的路徑長度來更新該次。(這裏十分關鍵,涉及到圖中存在環,結果路徑中也可能有環(本來可能沒有符合條件的路徑,可能在環中搖身一變再次到達該節點時已經變了顏色,例如:下面的案例))

案例:

5
[[0,1],[1,2],[2,3],[3,4]]
[[1,2],[2,3],[3,1]]
class Solution {
public:
    vector<int> shortestAlternatingPaths(int n, vector<vector<int>>& red_edges, vector<vector<int>>& blue_edges) {
        vector<vector<int>> edges(n, vector<int>(n, 0x3f3f3f3f));
        for(auto r : red_edges){
            edges[r[0]][r[1]] = 1;//紅邊用1表示
        }
        for(auto b : blue_edges){
            if(edges[b[0]][b[1]] == 1)
                edges[b[0]][b[1]] = -1;//紅藍用0表示
            else
                edges[b[0]][b[1]] = 0;//藍邊用-1表示
        }
        
        vector<vector<int>> dis(n, vector<int>(2, INT_MAX));
        dis[0] = {0, 0};
        auto copy_edges(edges);
        dfs(edges, dis, 0, 1, n);//1:表示開始時先走紅邊
        dfs(copy_edges, dis, 0, 0, n);//-1:表示開始時先走藍邊
        
        vector<int> res(n);
        for(int i = 0; i < n; ++i){   
            res[i] = min(dis[i][0], dis[i][1]);
            if(res[i] == INT_MAX)
                res[i] = -1;
        }
        return res;
    }
private:
    void dfs(vector<vector<int>> &edges, vector<vector<int>> &dis, int point, int flag, int n){
        for(int i = 0; i < n; ++i){
            if(dis[point][!flag] + 1 < dis[i][flag] && (edges[point][i] == flag || edges[point][i] == -1)){
                dis[i][flag] = dis[point][!flag] + 1;                
                dfs(edges, dis, i, !flag, n);
            }
        }
        
    }
    
};

深度分析

這道題無論用什麼方法,一定保證相鄰路徑的顏色不能相同,

  • bfs:更新路徑長度時,根據其所在的層次就能更新,因爲能到達該層,路徑一定是顏色相間的
  • dfs:一定要維護兩個數組,通過藍色邊到達該點和通過紅邊到達該點的路徑長度一定要分開計算
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章