DFS的運用(二分圖判定、無向圖的割頂和橋,雙連通分量,有向圖的強連通分量)
阿新 • • 發佈:2018-09-04
part str stack void div prev this 沒有 2-sat
一、dfs框架:
1 vector<int>G[maxn]; //存圖 2 int vis[maxn]; //節點訪問標記 3 void dfs(int u) 4 { 5 vis[u] = 1; 6 PREVISIT(u); //訪問節點u之前的操作 7 int d = G[u].size(); 8 for(int i = 0; i < d; i++)//枚舉每條邊 9 { 10 int v = G[u][i]; 11 if(!vis[v])dfs(v); 12 } 13 POSTVISIT(u); //訪問節點u之後的操作 14 }
二、無向圖連通分量
1 void find_cc() 2 { 3 current_cc = 0;//全局變量 連通塊編號 4 memset(vis, 0, sizeof(vis)); 5 for(int u = 0; u < n; u++)if(!vis[u])//依次檢查每個節點,如果未訪問過,說明它屬於一個新的連通分量,從該點dfs訪問整個連通分量 6 { 7 current_cc++; 8 dfs(u); 9 } 10 }
三、二分圖判定
調用之前,清空color數組,調用之前,先給color[u]賦值1
1 int color[maxn];//0表示未染色 1表示白色 2表示黑色 2 bool bipartite(int u) 3 { 4 for(int i = 0; i < G[u].size(); i++) 5 { 6 int v = G[u][i]; 7 if(color[u] == color[v])return false;//u v顏色一樣 8 if(!color[v]) 9 { 10 color[v] = 3 - color[u];//節點u與v染不同的顏色 11 if(!bipartite(v))return false; 12 } 13 } 14 return true; 15 }
四、無向圖的割點和橋
加入時間戳
int dfs_clock; void PREVISIT(int u){pre[u] = ++dfs_clock;} void POSTVISIT(int u){post[u] = ++dfs_clock;}
註意:求橋的時候註意重邊
五、無向圖的雙連通分量
點-雙連通分量
1 struct Edge 2 { 3 int u, v; 4 Edge(){} 5 Edge(int u, int v):u(u), v(v){} 6 }; 7 int pre[maxn];//時間戳數組 8 int iscut[maxn];//割點 9 int bccno[maxn];//點-雙連通分量編號 (割點的編號沒有意義) 10 int dfs_clock, bcc_cnt;//時間戳 雙連通分量編號 11 vector<int>G[maxn], bcc[maxn];//G存儲圖 bcc存儲每個雙連通分量的點 12 stack<Edge>S; 13 14 int dfs(int u, int fa) 15 { 16 int lowu = pre[u] = ++dfs_clock; 17 int child = 0; 18 for(int i = 0; i < G[u].size(); i++) 19 { 20 int v = G[u][i]; 21 Edge e(u, v); 22 if(!pre[v]) 23 { 24 S.push(e); 25 child++; 26 int lowv = dfs(v, u); 27 lowu = Min(lowu, lowv); 28 if(lowv >= pre[u]) 29 { 30 iscut[u] = 1; 31 bcc_cnt++; 32 bcc[bcc_cnt].clear(); 33 for(;;) 34 { 35 Edge x = S.top(); 36 S.pop(); 37 if(bccno[x.u] != bcc_cnt){bcc[bcc_cnt].push_back(x.u);bccno[x.u] = bcc_cnt;} 38 if(bccno[x.v] != bcc_cnt){bcc[bcc_cnt].push_back(x.v);bccno[x.v] = bcc_cnt;} 39 if(x.u == u && x.v == v)break; 40 } 41 } 42 } 43 else if(pre[v] < pre[u] && v != fa) 44 { 45 S.push(e); 46 lowu = Min(lowu, pre[v]); 47 } 48 } 49 if(fa < 0 && child == 1)iscut[u] = 0; 50 return lowu; 51 } 52 void find_bcc(int n)//求解點-雙連通分量 53 { 54 Mem(pre); 55 Mem(iscut); 56 Mem(bccno); 57 dfs_clock = bcc_cnt = 0; 58 for(int i = 0; i < n; i++)if(!pre[i])dfs(i, -1); 59 }
六、有向圖的強連通分量
1 vector<int>G[maxn]; 2 int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt; 3 stack<int>S; 4 void dfs(int u) 5 { 6 pre[u] = lowlink[u] = ++dfs_clock; 7 S.push(u); 8 for(int i = 0; i < G[u].size(); i++) 9 { 10 int v = G[u][i]; 11 if(!pre[v]) 12 { 13 dfs(v); 14 lowlink[u] = Min(lowlink[u], lowlink[v]); 15 } 16 else if(!sccno[v]) 17 { 18 lowlink[u] = Min(lowlink[u], pre[v]); 19 } 20 } 21 if(lowlink[u] == pre[u]) 22 { 23 scc_cnt++; 24 for(;;) 25 { 26 int x = S.top(); 27 S.pop(); 28 sccno[x] = scc_cnt; 29 if(x == u)break; 30 } 31 } 32 } 33 void find_scc(int n) 34 { 35 dfs_clock = scc_cnt = 0; 36 Mem(sccno); 37 Mem(pre); 38 for(int i = 0; i < n; i++)if(!pre[i])dfs(i); 39 }
七、2-SAT問題
1 struct TwoSAT 2 { 3 int n; 4 vector<int>G[maxn * 2]; 5 bool mark[maxn * 2]; 6 int S[maxn * 2], c; 7 bool dfs(int x) 8 { 9 if(mark[x^1])return false; 10 if(mark[x])return true; 11 mark[x] = true; 12 S[c++] = x; 13 for(int i = 0; i < G[x].size(); i++) 14 if(!dfs(G[x][i]))return false; 15 return true; 16 } 17 void init(int n) 18 { 19 this->n = n; 20 for(int i = 0; i < n * 2; i++)G[i].clear(); 21 Mem(mark); 22 } 23 //x = xval or y = yval 24 void add_clause(int x, int xval, int y, int yval) 25 { 26 x = x * 2 + xval; 27 y = y * 2 + yval; 28 G[x ^ 1].push_back(y); 29 G[y ^ 1].push_back(x); 30 } 31 bool solve() 32 { 33 for(int i = 0; i < n * 2; i += 2) 34 { 35 if(!mark[i] && !mark[i + 1]) 36 { 37 c = 0; 38 if(!dfs(i)) 39 { 40 while(c > 0)mark[S[--c]] = false; 41 if(!dfs(i + 1))return false; 42 } 43 } 44 } 45 return true; 46 } 47 };
DFS的運用(二分圖判定、無向圖的割頂和橋,雙連通分量,有向圖的強連通分量)