您的位置:首页 > 理论基础 > 计算机网络

POJ3352Road Construction(构造双连通图)sdut2506完美网络

2015-01-19 21:27 218 查看
构造双连通图:一个有桥的连通图,如何把它通过加边变成边双连通图

一个有桥的连通图,如何把它通过加边变成边双连通图?方法为首先求出所有的桥,然后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1。

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

题目: http://poj.org/problem?id=3352

题意:给你一个连通的无向图,现在问你最少在该图中添加几条边,能使得该图变成边双连通图?

我没有搞懂为什么至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,暂且记住吧。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#define N 10010
using namespace std;
struct node
{
int x,y,w,next,flag;
} eg[2*N];
stack<int>q;
int tt,head
,dfn
,low
,ti,n,m,be
,bridge
[2],tp;
int cnt,de
;
void init()
{
tt=0;
ti=0;
tp=0;
cnt=0;
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(be,0,sizeof(be));
memset(de,0,sizeof(de));
memset(bridge,0,sizeof(bridge));
while(!q.empty()) q.pop();
}
void add(int xx,int yy)
{
eg[tt].x=xx;
eg[tt].y=yy;
eg[tt].next=head[xx];
head[xx]=tt++;
}
void tarjan(int u,int fa)
{
dfn[u]=low[u]=++ti;
q.push(u);
for(int i=head[u]; i!=-1; i=eg[i].next)
{
int v=eg[i].y;
if(v==fa) continue;
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])//边(u,v)为桥,可以统计一个边连通分支
{
bridge[tp][0]=u;
bridge[tp++][1]=v;
++cnt;
int w;
do
{
w=q.top();
q.pop();
be[w]=cnt;
}
while(w!=v);//注意点u并没有出栈,因为点u属于另一个边连通分量
}
}
else
{
low[u]=min(dfn[v],low[u]);
}
}
}
int main()
{
int xx,yy;
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i=1; i<=m; i++)
{
scanf("%d%d",&xx,&yy);
add(xx,yy);
add(yy,xx);
}
tarjan(1,-1);
if(!q.empty())
{
++cnt;
int w;
do
{
w=q.top();
q.pop();
be[w]=cnt;
}
while(w!=1);
}
for(int i=0; i<tp; i++)
{
int u=bridge[i][0];
int v=bridge[i][1];
de[be[u]]++;
de[be[v]]++; //统计缩点后的的度
}
/*for(int i=0;i<tp;i++)
{
printf("bridge==%d %d\n",bridge[i][0],bridge[i][1]);
}
printf("cnt==%d\n",cnt);*/
int leaf=0;
for(int i=1; i<=cnt; i++)
{
if(de[i]==1)
leaf++;
}
printf("%d\n",(leaf+1)/2);
}
return 0;
}


第一次交的,需要用数组f
防止跨边,我感觉实际上这样是没用的,但大家代码都这么写我就贴一下吧,无向图是没有跨边的。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 1010
#define M 1010
#define min(a,b) ((a)<(b)?(a):(b))

int n,tot;
int head
,dfn
,low
,belong
,de
,stack
,bridge[M][2],ins
,dcnt,bcnt,top,bnum;
struct edge
{
int u,v,next;
}e[2*M];

void add(int u ,int v ,int k)
{
e[k].u = u; e[k].v = v; e[k].next = head[u]; head[u] = k++;
u = u^v;  v = u^v;  u = u^v;
e[k].u = u; e[k].v = v; e[k].next = head[u]; head[u] = k++;
}

void dfs(int u ,int fa)
{
dfn[u] = low[u] = ++dcnt;
stack[++top] = u; ins[u] = 1;
for(int k=head[u]; k!=-1; k=e[k].next)
{
int v = e[k].v;
if(v == fa) continue;
if(!dfn[v]) //树边
{
dfs(v,u);
low[u] = min(low[u] , low[v]);
if(low[v] > dfn[u]) //边(u,v)为桥,可以统计一个边连通分支
{
//保存桥
bridge[bnum][0] = u;
bridge[bnum++][1] = v;

++bcnt;
while(true)
{
int x = stack[top--];
ins[x] = 0;
belong[x] = bcnt;
if(x == v) break;
}//注意点u并没有出栈,因为点u属于另一个边连通分量
}
}
else if(ins[v]) //后向边
low[u] = min(low[u] , dfn[v]);
//横叉边为(dfn[v] && !ins[v]),跳过
}
}

void solve()
{
memset(dfn,0,sizeof(dfn));
memset(de,0,sizeof(de));
memset(ins,0,sizeof(ins));
dcnt = bcnt = top = bnum = 0;
dfs(1,-1);
if(top)
{
++bcnt;
while(true)
{
int x = stack[top--];
ins[x] = 0;
belong[x] = bcnt;
if(x == 1) break;
}
}

for(int i=0; i<bnum; i++) //取出所有的桥
{
int u = bridge[i][0];
int v = bridge[i][1];
de[belong[u]]++;
de[belong[v]]++;
//统计缩点后的的度
}
int leaf = 0;
for(int i=1; i<=bcnt; i++)
if(de[i] == 1)
leaf++;
cout << (leaf+1)/2 << endl;

//可以把下面的注释去掉,看看记录的内容,帮组理解
/*
for(int u=1; u<=n; u++)
cout << u << "[" << belong[u] << "]" << endl;
for(int i=1; i<=bcnt; i++)
cout << "[" << de[i] << "]" << endl;
for(int i=0; i<bnum; i++)
{
int u = bridge[i][0], v = bridge[i][1];
printf("桥: %d %d\n",u,v);
printf("缩点后的边: %d %d\n",belong[u] , belong[v]);
}
*/
}

int main()
{
while(cin >> n >> tot)
{
memset(head,-1,sizeof(head));
int u,v,k=0;
for(int i=0; i<tot; i++,k+=2)
{
cin >> u >> v;
add(u,v,k);
}
solve();
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: