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

原文地址:https://www.cnblogs.com/fzl194/p/9580306.html

时间: 2024-11-03 05:34:20

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

连通分量 无向图的割顶和桥 无向图的双连通分量 有向图的强连通分量

时间戳 dfs_clock :说白了就是记录下访问每个结点的次序.假设我们用 pre 保存,那么如果 pre[u] > pre[v], 那么就可以知道先访问的 v ,后访问的 u . 现在给定一条边, (u, v), 且 u 的祖先为 fa, 如果有 pre[v] < pre[u] && v != fa, 那么 (u, v) 为一条反向边. 1 求连通分量: 相互可达的节点称为一个连通分量: #include <iostream> #include <cstd

无向图求割顶与桥

无向图求割顶与桥 对于无向图G,如果删除某个点u后,连通分量数目增加,称u为图的关节点或割顶.对于连通图,割顶就是删除之后使图不再连通的点.如果删除边(u,v)一条边,就可以让连通图变成不连通的,那么边(u,v)是桥. 具体的概念和定义比较多,在刘汝佳<<训练指南>>P312-314页都有详细的介绍. 下面来写求无向图割顶和桥的DFS函数.我们令pre[i]表示第一次访问i点的时间戳,令low[i]表示i节点及其后代所能连回(通过反向边)的最早祖先的pre值. 下面的dfs函数返回

无向图的割顶和桥,无向图的双连通分量入门详解及模板 -----「转载」

https://blog.csdn.net/stillxjy/article/details/70176689 割顶和桥:对于无向图G,如果删除某个节点u后,连通分量数目增加,则称u为图的割顶:如果删除某条边后,连通分量数目增加,则称该边为图的桥.对于连通图删除割顶或桥后都会使得图不再连通 以下我,我们利用dfs的性质来快速找出一个连通图中的所有的割顶和桥 首先我们要引入”时间戳”这个概念: 时间戳:表示在进行dfs时,每个节点被访问的先后顺序.每个节点会被标记两次,分别用pre[],和post

无向图的割顶和桥

割顶: 关键点,删掉这个点后,图的连通分量 + 1: 桥: 在割顶的基础上,发现删除 (u,v) 这条边,图就变成非连通的了. 如何找出所有割顶和桥: 时间戳: 在无向图的基础上,DFS建树的过程中,各点进栈和出栈的时间 dfs_clock,进栈的时间 pre[],出栈的时间 post[] 在DFS程序中的体现就是: void previst(int u) { pre[u]= ++dfs_clock; } void postvist(int u) { post[u] = ++dfs_clock;

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

割顶:对于无向图G,如果删除某个点u后,连通分量的数目增加, 称u为图的割顶.对于连通图,割顶就是删除之后使图不再连通的点. 割顶的求解依如下定理: 在无向连通图G的DFS树中,非根结点u是G的割顶当且仅当u存在一个子节点v,使得v及其所有后代都没有反向边连回u的祖先(连回u)不算. 算法实现: 采用时间戳,在dfs遍历的过程中给每个节点u均标记以前序时间戳pre[u],设low[u]为u及其后代所能连回的最早的祖先的pre值,则定理中的条件就可以简写成结点u存在一个子结点v,使得 low[v]

无向图求割顶和桥总结

1.求能够分成几个联通分量什么的一般都在dfs中间那里if(...>...) cnt[i],iscut[i]维护一下就OK了. 2.根结点特别需要注意. 好像就没了→_→

无向图的割顶和桥(tarjan模板)

#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define maxn 7500 #define inf 0x3f3f3f3f int first[maxn],to[maxn],nxt[maxn],e; int pre[maxn],low[maxn]; int clock; int iscut[maxn]; void

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

#include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int N = 250; int head[N], low[N], dfn[N], fa[N]; int n, m, now = 1, Tarjan_clock; bool is_cut[N]; struct Node{ int u, v, nxt; }E[N]; inline int read()

【模板】无向图的割顶

无向图的割顶: Vector <int> G[] :邻接表存图 Int pre[] :存储时间戳 Int low[] : u及其后代所能连回的最早的祖先的pre值 Int iscut[] : =true表示是割顶,=false不是割顶 Dfs函数在主函数调用时,fa预设为-1. vector <int> G[MAXN]; int pre[MAXN],iscut[MAXN],low[MAXN],dfs_clock; int dfs(int u,int fa) { int lowu=p