图论--无向图点双连通分量模板

对于一个无向图,如果一个点集,它内部的任意一个点对之间,至少有两条点完全不重复的路径,那么这个点集就是原图的一个点双连通分量,而点双联通分量之间是由割点隔开,割点就是如果删去这个点,原图的连通块数会增加,那么这个点就是割点。

通过tarjan算法,我们可以用一次 dfs 标记出所有的割点以及所有双连通分量。

注释版:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stack>
 4 #include<algorithm>
 5 #include<vector>
 6 using namespace std;
 7
 8 const int maxn=1e5+5;
 9 const int maxm=1e5+5;
10
11 int head[maxn],point[maxm],nxt[maxm],size;
12 int n,t,bcccnt;
13 //n点数,t是dfs的时间轴,bcccnt是双连通分量个数
14 int stx[maxn],low[maxn],bcc[maxn],cut[maxn];
15 //stx是节点在dfs时间轴的位置,low是该点能够通过后继节点到达的最远祖先,bcc是某个点所属的双连通分量编号(割点的编号无效),cut是是否为割点
16 vector<int>bccs[maxn];
17 //双连通分量内的节点
18 stack<int>S;
19
20 void init(){
21     memset(head,-1,sizeof(head));
22     size=0;
23 }
24
25 void add(int a,int b){
26     point[size]=b;
27     nxt[size]=head[a];
28     head[a]=size++;
29     point[size]=a;
30     nxt[size]=head[b];
31     head[b]=size++;
32 }
33
34 void dfs(int s,int pre){
35     stx[s]=low[s]=++t;        //记录点的时间轴标号,初始化能访问到的最远祖先节点是自己
36     S.push(s);
37     int son=0;                //为了判定根节点是否是割点
38     for(int i=head[s];~i;i=nxt[i]){
39         int j=point[i];
40         if(!stx[j]){
41             son++;
42             dfs(j,s);
43             low[s]=min(low[s],low[j]);    //用子节点的low值更新自己
44             if(low[j]>=stx[s]){            //如果子节点最远只能访问自己或后继节点,则出现双连通分量
45                 cut[s]=1;                //自己是割点
46                 bcccnt++;
47                 bccs[bcccnt].clear();
48                 while(1){
49                     int u=S.top();S.pop();
50                     bcc[u]=bcccnt;
51                     bccs[bcccnt].push_back(u);
52                     if(u==j)break;
53                 }
54                 bcc[s]=bcccnt;
55                 bccs[bcccnt].push_back(s);
56             }
57         }
58         else if(j!=pre)low[s]=min(stx[j],low[s]);
59     }
60     if(pre==-1&&son==1)cut[s]=0;        //若根节点只有一个子节点,则不是割点
61 }
62
63 void setbcc(){
64     memset(cut,0,sizeof(cut));
65     memset(stx,0,sizeof(stx));
66     memset(bcc,0,sizeof(bcc));
67     t=bcccnt=0;
68     for(int i=1;i<=n;++i)if(!stx[i])dfs(i,-1);
69 }
70
71 int main(){
72     int m;
73     scanf("%d%d",&n,&m);
74     init();
75     while(m--){
76         int a,b;
77         scanf("%d%d",&a,&b);
78         add(a,b);
79     }
80     setbcc();
81     for(int i=1;i<=n;++i){
82         printf("%d:%d %d\n",i,cut[i],bcc[i]);
83     }
84     for(int i=1;i<=bcccnt;++i){
85         printf("%d:",i);
86         for(int j=0;j<bccs[i].size();++j){
87             printf("%d ",bccs[i][j]);
88         }
89         printf("\n");
90     }
91     return 0;
92 }

无注释版:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stack>
 4 #include<algorithm>
 5 #include<vector>
 6 using namespace std;
 7
 8 const int maxn=1e5+5;
 9 const int maxm=1e5+5;
10
11 int head[maxn],point[maxm],nxt[maxm],size;
12 int n,t,bcccnt;
13 int stx[maxn],low[maxn],bcc[maxn],cut[maxn];
14 vector<int>bccs[maxn];
15 stack<int>S;
16
17 void init(){
18     memset(head,-1,sizeof(head));
19     size=0;
20 }
21
22 void add(int a,int b){
23     point[size]=b;
24     nxt[size]=head[a];
25     head[a]=size++;
26     point[size]=a;
27     nxt[size]=head[b];
28     head[b]=size++;
29 }
30
31 void dfs(int s,int pre){
32     stx[s]=low[s]=++t;
33     S.push(s);
34     int son=0;
35     for(int i=head[s];~i;i=nxt[i]){
36         int j=point[i];
37         if(!stx[j]){
38             son++;
39             dfs(j,s);
40             low[s]=min(low[s],low[j]);
41             if(low[j]>=stx[s]){
42                 cut[s]=1;
43                 bcccnt++;
44                 bccs[bcccnt].clear();
45                 while(1){
46                     int u=S.top();S.pop();
47                     bcc[u]=bcccnt;
48                     bccs[bcccnt].push_back(u);
49                     if(u==j)break;
50                 }
51                 bcc[s]=bcccnt;
52                 bccs[bcccnt].push_back(s);
53             }
54         }
55         else if(j!=pre)low[s]=min(stx[j],low[s]);
56     }
57     if(pre==-1&&son==1)cut[s]=0;
58 }
59
60 void setbcc(){
61     memset(cut,0,sizeof(cut));
62     memset(stx,0,sizeof(stx));
63     memset(bcc,0,sizeof(bcc));
64     t=bcccnt=0;
65     for(int i=1;i<=n;++i)if(!stx[i])dfs(i,-1);
66 }
67
68 int main(){
69     int m;
70     scanf("%d%d",&n,&m);
71     init();
72     while(m--){
73         int a,b;
74         scanf("%d%d",&a,&b);
75         add(a,b);
76     }
77     setbcc();
78     for(int i=1;i<=n;++i){
79         printf("%d:%d %d\n",i,cut[i],bcc[i]);
80     }
81     for(int i=1;i<=bcccnt;++i){
82         printf("%d:",i);
83         for(int j=0;j<bccs[i].size();++j){
84             printf("%d ",bccs[i][j]);
85         }
86         printf("\n");
87     }
88     return 0;
89 }
时间: 2024-08-04 03:16:30

图论--无向图点双连通分量模板的相关文章

无向图双连通分量 模板

//点-双连通分量模板. #include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<stack> using namespace std; struct Edge { int u,v; };//u,v是边的两个端点 const int maxn=100005; int n,m; int pre[maxn],iscut[maxn],bccno[ma

点-双连通分量模板

by:白书 #define M 10000 int pre[M],dfs_clock,iscut[M],low[M],bcc_cnt,bccno[M]; vector<int>G[M],bcc[M]; struct Edge { int u,v; Edge(int from,int to) { u=from; v=to; } }; stack<Edge> S; int dfs(int u,int fa) { int lowu=pre[u]=++dfs_clock; int chil

hdu3844 Mining Your Own Business,无向图的双连通分量

点击打开链接 无向图的双连通分量 #include<cstdio> #include<stack> #include<vector> #include<map> #include<algorithm> #include<cstring> #pragma comment(linker, "/STACK:102400000,102400000") using namespace std; typedef long lo

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

时间戳 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

hdu Caocao&#39;s Bridges(无向图边双连通分量,找出权值最小的桥)

1 /* 2 题意:给出一个无向图,去掉一条权值最小边,使这个无向图不再连同! 3 4 tm太坑了... 5 1,如果这个无向图开始就是一个非连通图,直接输出0 6 2,重边(两个节点存在多条边, 权值不一样) 7 3,如果找到了桥的最小权值为0,也就是桥上的士兵数为0,那么还是要最少派一个 8 士兵过去炸掉桥! 9 10 思路:假设每两个节点最多只有一条边进行相连! 11 进行tarjan算法,如果该算法调用了超过2次,说明这个原图就是不连通的! 12 否则在tarjan算法中将桥存起来!然后

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

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

UVALive - 3523 Knights of the Round Table(无向图的双连通分量)

题目大意:有n个骑士经常举行圆桌会议,每次圆桌会议至少要有3个骑士参加(且每次参加的骑士数量是奇数个),且所有互相憎恨的骑士不能坐在圆桌旁的相邻位置,问有多少个骑士不可能参加任何一个会议 解题思路:以骑士为点建立无向图G.如果两个骑士可以相邻(即他们并不互相憎恨),即可连一条边. 则题目就转化为求不在任何一个简单奇圈上的结点个数 首先,圈就是一个双连通的分量,所以第一件事就是将所有的双连通分量求出来,接着再判定这个双连通分量是不是奇圈 奇圈的判定就是用二分图染色判定,如果某个圈能被二分图染色,那

LA 5135 井下矿工(点—双连通分量模板题)

https://vjudge.net/problem/UVALive-5135 题意:在一个无向图上选择尽量少的点涂黑,使得任意删除一个点后,每个连通分量至少有一个黑点. 思路: 首先dfs遍历求出割顶和双连通分量,并把每个连通分量保存下来. 接下来分情况讨论: 如果一个点—双连通分量只有一个割顶,在该分量中必须将一个非割顶涂黑. 如果一个点—双连通分量有2个及以上的割顶,不需要涂黑. 如果整个图没有割顶,则至少需要涂黑两个点.(因为有可能删除的就是涂黑的点) 1 #include<iostre

poj 2942 Knights of the Round Table(无向图的双连通分量+二分图判定)

#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #