您的位置:首页 > 其它

无向图的双连通块(点的双连通)&&边的双连通算法

2015-07-23 21:51 429 查看
无向图的双连通块

题目描述

给出一个无向图,求其中双连通块的个数以及其中最大的双连通块。

输入

第1行:2个整数n和m,n表示图的结点个数,m表示图中边的数量 (1<=n<=100000, 1<=m<=1000000)

接下来m行,每行2个整数,表示一条边

输出

第1行:1个整数,表示整个图中双连通块的数量

第2行:1个整数,表示整个图中最大的双连通块的边的条数

接下来依次输出最大的双连通块的各条边,按字典序输出,即按边的端点编号从小到大输出,每条边先输出编号小的端点,再输出编号大的端点

若有多个边数相同的最大双连通块,输出字典序最小的一个

样例输入

9 10

1 2

4 1

2 3

4 2

1 5

5 6

6 7

6 8

7 9

8 7

样例输出

6

3

1 2

1 4

2 4

把割点切开就好了..

#include<cstdio>
#include<stack>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 100000
#define MAXM 1000000
struct node{
int v;
node *next;
}edge[MAXM*2+10],*adj[MAXN+10],*ecnt=&edge[0];
int n,m,dfn[MAXN+10],low[MAXN+10],dcnt,block;
int ccnt,acnt;
stack<pair<int , int> > e;
struct list{
pair<int , int> p;
}ans[MAXM+10],cmp[MAXM+10];
void addedge(int u,int v)
{
node *p=++ecnt;
p->v=v;
p->next=adj[u];
adj[u]=p;
}
void read()
{
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
}
bool compare(list x,list y)
{
if(x.p.first==y.p.first) return x.p.second<y.p.second;
return x.p.first<y.p.first;
}
void cut_block(int u,int v)
{
ccnt=0;
pair<int , int> tmp;
do{
tmp=e.top(); e.pop();
cmp[++ccnt].p=tmp;
}while(!(tmp.first==u&&tmp.second==v));
for(int i=1;i<=ccnt;i++)
if(cmp[i].p.first>cmp[i].p.second)
swap(cmp[i].p.first,cmp[i].p.second);
sort(cmp+1,cmp+ccnt+1,compare);
bool flag=false;
if(ccnt>acnt) flag=true;
else if(ccnt==acnt){
for(int i=1;i<=ccnt;){
if(cmp[i].p.first>ans[i].p.first) break;
else if(cmp[i].p.first<ans[i].p.first) flag=true;
else if(cmp[i].p.second<ans[i].p.second) flag=true;
else if(cmp[i].p.second>ans[i].p.second) break;
else i++;
if(flag) break;
}
}
if(flag){
acnt=ccnt;
memcpy(ans,cmp,sizeof(cmp));
}
}
void dfs(int u,int fa)
{
dfn[u]=low[u]=++dcnt;
for(node *p=adj[u];p;p=p->next){
int v=p->v;
if(!dfn[v]){
e.push(make_pair(u,v));
dfs(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
cut_block(u,v);
block++;
}
}
else if(v!=fa&&dfn[u]>dfn[v]){
low[u]=min(low[u],dfn[v]);
e.push(make_pair(u,v));
}
}
}
int main()
{
read();
dfs(1,-1);
printf("%d\n%d\n",block,acnt);
for(int i=1;i<=acnt;i++)
printf("%d %d\n",ans[i].p.first,ans[i].p.second);
return 0;
}


边的双连通即为删去割边后的连通块

**点双连通即为没有割点的块,边亦如是
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: