1. 程式人生 > >DFS的運用(二分圖判定、無向圖的割頂和橋,雙連通分量,有向圖的強連通分量)

DFS的運用(二分圖判定、無向圖的割頂和橋,雙連通分量,有向圖的強連通分量)

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的運用(二分圖判定、無向圖的割頂和橋,雙連通分量,有向圖的強連通分量)