您的位置:首页 > 其它

RevolC FaeLoN UVA - 10972

2017-05-25 12:02 561 查看

RevolC FaeLoN UVA - 10972

图论·边-双连通分量

http://blog.csdn.net/l123012013048/article/details/47373065

题目大意:

把无向图的每一条边变成一条有向边,形成一个并加入最少的有向边,让新图称为一个强连通分量。问最小加边数。

题解:

求出所有的边-双连通分量,把他们缩点,得到多棵无根树森林。

树上的点是边-双连通分量,边是桥。

对于一个连通块,如果它的节点个数>1:

统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

另外,两个连通块的互相连接并不需要额外的边。

只要把给他们加的边各自拆开一条,就能把他们连起来连到一起。

对于一个孤立的点,它需要一条边:

如果有n个点,要求在这n个点间添加有向边,使得这n个点变成强连通,那么需要添加的边的数量为n,均摊每个点一条。

有了上面这个结论,求的时候就比较好办了,处理的时候,只需要统计出所有度为1和度为0的缩点的数量即可 。

假设度为1的数量为A (叶子结点,连通图里面的)

度为2的数量为B(孤立的点)

那么所需要连的边就是(A + 1) / 2 + B

注意求边-双连通分量的时候,不能把当前u取出来,而是只取到v。

在dfs外面要把栈里剩下的元素作为一个新的双联通分量。

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
#define MP make_pair
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl
using namespace std;
typedef pair<int,int> pii;
const int N = 2017;
const int M = 20017;

int n,m;

struct Edge{
int u,v,next;
}e[M];
int head
, ec=0;
void clear(){ memset(head,0,sizeof(head)); ec=0; }
void add(int a,int b){
ec++; e[ec].u=a; e[ec].v=b; e[ec].next=head[a]; head[a]=ec;
}

int belong
,bridge[M][2],low
,dfn
,du
,tim,bcc_cnt,bridge_cnt;
stack<int> s;

void dfs(int u,int pa){
low[u]=dfn[u]=++tim;
s.push(u);
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(!dfn[v]){
dfs(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u]){
bridge_cnt++;
bridge[bridge_cnt][0]=u; bridge[bridge_cnt][1]=v;
bcc_cnt++;
while(true){
int x=s.top(); s.pop();
belong[x]=bcc_cnt;
if(x==v) break;
}
}
}
else if(dfn[v]<dfn[u] && v!=pa){
low[u]=min(low[u],dfn[v]);
}
}
}

void find_bcc(){
bcc_cnt=0; tim=0; bridge_cnt=0;
memset(dfn,0,sizeof(dfn));
memset(belong,0,sizeof(belong));
for(int i=1;i<=n;i++){
if(!dfn[i]){
dfs(i,-1);
bcc_cnt++;
while(!s.empty()){
int x=s.top(); s.pop();
belong[x]=bcc_cnt;
}
}
}
}

int main(){
freopen("a.in","r",stdin);
while(~scanf("%d%d",&n,&m) && (n||m)){
clear();
int a,b;
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
add(a,b); add(b,a);
}
find_bcc();
//      D(bcc_cnt); E;
//      for(int i=1;i<=n;i++){
//          D(belong[i]);
//      } E;
if(bcc_cnt==1){
printf("0\n");
continue;
}
memset(du,0,sizeof(du));
for(int i=1;i<=bridge_cnt;i++){
du[belong[bridge[i][0]]]++;
du[belong[bridge[i][1]]]++;
}
//      for(int i=1;i<=bcc_cnt;i++) D(du[i]); E;
int A=0,B=0;
for(int i=1;i<=bcc_cnt;i++){
if(du[i]==1) A++;
if(du[i]==0) B++;
}
//      D(ans); E;
printf("%d\n",(A+1)/2+B);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: