DFS的运用(二分图判定无向图的割顶和桥,双连通分量,有向图的强连通分量)

Posted fzl194

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DFS的运用(二分图判定无向图的割顶和桥,双连通分量,有向图的强连通分量)相关的知识,希望对你有一定的参考价值。

一、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的运用(二分图判定无向图的割顶和桥,双连通分量,有向图的强连通分量)的主要内容,如果未能解决你的问题,请参考以下文章

无向图的割顶和桥的性质 以及双连通分量的求解算法

无向图的割顶和桥

连通图基本知识

2018/2/11 每日一学 无向图割顶和桥

Tarjan 算法求无向图的割顶和桥

无向图求割顶和桥总结