題解:咱們用一個color數組標記點的顏色,而後對每個點作bfs,若是兩個點間有邊,而且另一個點沒有被染色,就把另一個點染色成相反色,若是另一個點有顏色,並且顏色和當前結點相同,那麼確定不是二分圖。html
1 class Solution { 2 public: 3 bool possibleBipartition(int N, vector<vector<int>>& dislikes) { 4 N1 = N; 5 const int edges = dislikes.size(); 6 if (edges == 0) {return true;} 7 for (auto& p : dislikes) { 8 stDis.insert(make_pair(p[0], p[1])); 9 } 10 vector<int> color(N + 1, -1); 11 for (int i = 1; i < N+1; ++i) { 12 if (color[i] == -1 && !bfs(i, color)) { 13 return false; 14 } 15 } 16 return true; 17 } 18 bool bfs(int p, vector<int>& color) { 19 queue<int> que; 20 que.push(p); 21 color[p] = 1; 22 while (!que.empty()) { 23 int node = que.front(); 24 que.pop(); 25 for (int i = 1; i <= N1; ++i) { 26 if (i == node) { continue; } 27 pair<int, int> e = make_pair(min(i, node), max(i, node)); 28 if (stDis.find(e) != stDis.end()) { 29 if (color[i] == -1) { 30 color[i] = 1 - color[node]; 31 que.push(i); 32 } else if (color[i] == color[node]) { 33 return false; 34 } 35 } 36 } 37 } 38 return true; 39 } 40 int N1; 41 set<pair<int, int>> stDis; 42 };
這個解法巨慢, 1700+ms。看有沒有更快的方法。node
solution 裏面給了 dfs 的方法,80msios
就是先建圖,由於是DAG,注意雙邊。而後從一個沒有染過色的點開始,dfs染色,若是相鄰結點有染色,判斷是否衝突,若是相鄰結點沒有染色,就把它染成相反色。算法
1 //dfs 方法 建圖的時候,注意是雙邊都要加,這是DAG 2 class Solution { 3 public: 4 bool possibleBipartition(int N, vector<vector<int>>& dislikes) { 5 vector<int> temp(N + 1, -1); 6 color = temp; 7 m = dislikes.size(); 8 edges.resize(N + 1); 9 for (const auto& p : dislikes) { 10 edges[p[0]].push_back(p[1]); 11 edges[p[1]].push_back(p[0]); 12 } 13 for (int i = 1; i < N+1; ++i) { 14 if (color[i] == -1 && !dfs(i, 0)) { 15 return false; 16 } 17 } 18 return true; 19 } 20 bool dfs(int node, int c) { 21 if (color[node] != -1) { return color[node] == c; } 22 color[node] = c; 23 for (const auto& p : edges[node]) { 24 if (!dfs(p, 1 - c)) { 25 return false; 26 } 27 } 28 return true; 29 } 30 vector<int> color; 31 vector<vector<int>> edges; 32 int m; 33 };
從新寫了一個bfs,也能80ms過,看來是第一個方法很差,沒有雙向建圖數組
1 //bfs 方法 建圖的時候,注意是雙邊都要加,這是DAG 2 class Solution { 3 public: 4 bool possibleBipartition(int N, vector<vector<int>>& dislikes) { 5 vector<int> temp(N + 1, -1); 6 color = temp; 7 m = dislikes.size(); 8 edges.resize(N + 1); 9 for (const auto& p : dislikes) { 10 edges[p[0]].push_back(p[1]); 11 edges[p[1]].push_back(p[0]); 12 } 13 for (int i = 1; i < N+1; ++i) { 14 if (color[i] != -1) { continue; } 15 queue<int> que; 16 que.push(i); 17 color[i] = 0; 18 while (!que.empty()) { 19 int node = que.front(); que.pop(); 20 for (auto& p : edges[node]) { 21 if (color[p] == color[node]) {return false;} 22 if (color[p] == -1) { 23 color[p] = 1 - color[node]; 24 que.push(p); 25 } 26 } 27 } 28 } 29 return true; 30 } 31 vector<int> color; 32 vector<vector<int>> edges; 33 int m; 34 };
基本概念和定義:若是圖中存在有向環,則不存在拓撲排序,反之則存在。咱們把不包含有向環的有向圖稱爲有向無環圖(directed acyclic graph, DAG)。能夠藉助 dfs 完成拓撲排序:在訪問完一個結點以後把它放在當前拓撲排序的首部。(想一想爲啥不是尾部)數據結構
這裏用到了一個 c 數組, c[u] = 0 表示歷來沒有訪問過(歷來沒有調用過 dfs(u) );c[u] = 1 表示已經訪問過,而且還遞歸訪問過它的全部子孫(即dfs(u)曾經被調用過,並已經返回);c[u] = -1 說明正在訪問(即遞歸調用dfs(u)正在棧幀中,還沒有返回)。 ide
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cstdlib> 5 6 using namespace std; 7 8 int c[100] = {0}; 9 int topo[100] = {0}, t; 10 int g[105][105]; 11 int n; //n個結點 12 13 bool dfs(int u) { 14 c[u] = -1; 15 for (int v = 0; v < n; ++v) { 16 if (g[u][v]) { 17 if (c[v] < 0) { return false; } //說明有環 18 else if (!c[v] && !dfs(v)) {return false;} 19 } 20 } 21 c[u] = 1; 22 topo[--t] = u; 23 return true; 24 } 25 26 bool toposort() { 27 t = n; 28 memset(c, 0, sizeof(c)); 29 for (int u = 0; u < n; ++u) { 30 if (!c[u]) { 31 if (!dfs(u)) { 32 return false; 33 } 34 } 35 } 36 return true; 37 } 38 39 int main(int argc, char *argv[]) { 40 n = 5; 41 memset(g, 0 , sizeof(g)); 42 g[0][1] = 1; 43 g[0][3] = 1; 44 g[3][4] = 1; 45 //g[4][3] = 1; 檢測環 46 bool ret = toposort(); 47 if (ret) { 48 for (int i = 0; i < n; ++i) { 49 printf("%d ", topo[i]); 50 } 51 printf("\n"); 52 } 53 if (!ret) { 54 printf("no ans\n"); 55 } 56 return 0; 57 }
額外補充:如何判斷toposort的惟一性,看是否多於一個結點的入度爲 0。ui
2019年1月20日,補充topologic sort 的 bfs 版本。算法流程以下:spa
(1)建圖(鄰接鏈表) (2)計算入度 (3)找出全部入度爲 0 的點加入隊列 (4)開始bfs3d
1 class Solution { 2 public: 3 vector<int> findOrder(int numCourses, vector<pair<int, int>>& prerequisites) { 4 vector<vector<int>> g = initGraph(numCourses, prerequisites); 5 vector<int> degree = calDegree(g); 6 vector<int> ret(numCourses, 0); 7 queue<int> que; 8 for (int i = 0; i < degree.size(); ++i) { 9 if (degree[i] == 0) { 10 que.push(i); 11 } 12 } 13 for (int i = 0; i < numCourses; ++i) { 14 if (que.empty()) { 15 return vector<int>(); 16 } 17 int cur = que.front(); que.pop(); 18 ret[i] = cur; 19 for (auto e : g[cur]) { 20 if (--degree[e] == 0) { 21 que.push(e); 22 } 23 } 24 } 25 return ret; 26 } 27 private: 28 vector<vector<int>> initGraph(int n, vector<pair<int, int>>& pre) { 29 vector<vector<int>> g(n, vector<int>()); 30 for (auto e : pre) { 31 int start = e.second, end = e.first; 32 g[start].push_back(end); 33 } 34 return g; 35 } 36 vector<int> calDegree(vector<vector<int>>& g) { 37 vector<int> d(g.size(), 0); 38 const int n = g.size(); 39 for (auto e : g) { 40 for (auto v : e) { 41 d[v]++; 42 } 43 } 44 return d; 45 } 46 };
3. 最小生成樹
http://www.javashuo.com/article/p-mlxupbrq-ms.html
4. 最短路
http://www.javashuo.com/article/p-mlxupbrq-ms.html
5. 如何在一個無向圖上找環上的全部結點。(檢測環能夠直接Union Find)
這個思路是從kickstart 2018 Round C 的 Problem A 裏面來的。
咱們能夠用bfs作,O(N)的算法複雜度,Kahn的算法思想。咱們先把圖上全部度爲 1 的結點放進隊列裏面,而後從圖上刪除這個結點(就是從隊列裏面pop出來的時候,這個結點的度設置爲 0)。刪除了當前結點以後,咱們遍歷當前結點的沒有訪問過的相鄰結點(此時相鄰結點的度也應該減一),減一後看他們的度是否爲 1,若是度爲1,就把相鄰結點放進隊列裏面。最後隊列空了的時候,度不爲 0 的點就全在環上。