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 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 isbridge[maxm];//
 9 int bccno[maxn];//边-双连通分量编号 (任意两个无交集)
10 int low[maxn];//low[u]为u后代可以返回的最早祖先值
11 int dfs_clock, bcc_cnt;//时间戳 双连通分量编号
12 vector<Edge>e;//存边
13 vector<int>G[maxn], bcc[maxn];//G存储图 bcc存储每个双连通分量的点
14 //G存边在e中的下标
15 void init(int n)
16 {
17     for(int i = 0; i < n; i++)G[i].clear();
18     e.clear();
19 }
20 void addedge(int u, int v)//双向边
21 {
22     e.push_back(Edge(u, v));
23     G[u].push_back(e.size() - 1);
24     e.push_back(Edge(v, u));
25     G[v].push_back(e.size() - 1);//正好满足双向边^操作
26 }
27 void dfs1(int u, int fa)//找出所有的桥
28 {
29     low[u] = pre[u] = ++dfs_clock;
30     for(int i = 0; i < G[u].size(); i++)
31     {
32         int v = e[G[u][i]].v;
33         if(!pre[v])
34         {
35             dfs1(v, u);
36             low[u] = Min(low[u], low[v]);
37             if(low[v] > pre[u])//是桥,双向边均标记一下
38             {
39                 isbridge[G[u][i]] = isbridge[G[u][i]^1] = 1;
40             }
41         }
42         else if(pre[v] < pre[u] && v != fa)
43         {
44             low[u] = Min(low[u], pre[v]);
45         }
46     }
47 }
48 void dfs2(int u)//dfs染色找边-双连通分量即可
49 {
50     pre[u] = 1;//vis数组标记
51     bccno[u] = bcc_cnt;
52     bcc[bcc_cnt].push_back(u);
53     for(int i = 0; i < G[u].size(); i++)
54     {
55         int v = e[G[u][i]].v;
56         if(isbridge[G[u][i]])continue;//不走桥
57         if(!pre[v])dfs2(v);
58     }
59 }
60 void find_bcc(int n)//求解边-双连通分量
61 {
62     Mem(pre);Mem(isbridge);Mem(bccno);Mem(low);
63     dfs_clock = bcc_cnt = 0;
64     for(int i = 0; i < n; i++)if(!pre[i])dfs1(i, -1);
65     Mem(pre);
66     for(int i = 0; i < n; i++)if(!pre[i]){bcc_cnt++; bcc[bcc_cnt].clear(); dfs2(i);}
67 }

 

 

六、有向图的强连通分量

 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 };

 

posted @ 2018-09-03 23:39  _努力努力再努力x  阅读(606)  评论(0编辑  收藏  举报