学堂 学堂 学堂公众号手机端

POJ-3352无向图的割和桥以及双连通分量

lewis 1年前 (2024-04-16) 阅读数 14 #技术


双连通分量是指图中每两个点都有两条完全不同的路径可到达..也就是去掉这个图的任意一个边一个点...两两之间依然可达..


图论中的桥...在有向图中是两个连通分量之间唯一的边(如果有多条那么都不是桥)...在无向图中是两个双连通分量之间的唯一边...

而割指的是割点..割点肯定是和桥的端点...但桥的端点不一是割点..如:


(1,4),(2,4),(3,4),(4,5),(4,6),(4,7)都是桥..所有点都是桥的端点...但是只有4是割点...1,2,3,5,6,7都不是割点..

去掉一个连通图中的一个桥或者割点..这个连通图就将成为不连通的多个连通图..

求无向图中的桥的方法是有Tarjan发明的..Byvoid大大的讲解很给力:​​http://www.byvoid.com/blog/biconnect/​​

无向图的Tarjan和有向图求强连通分量的Tarjan很像...注意几个不同...

如果要求桥和割点的话..做完Tarjan..扫描所有的边,有 DFN ( 终点 ) < LOW ( 起点 ) 的边就是桥...这条边的"终点"就是割点..

回到POJ3352...题目给出了连通的无向图..并且两点直接最多只有一条边...求的是加几条边能使得该无向图整个变成一个双连通图..

那么先对图做无向图的Tarjan...得到每个点所在的双连通记录在Low里....可以想象一下..如果把所有的双连通分量分别缩成一个点...那么整个图就是一棵树了..如果给一棵树..要史其成为一个双连通图...那么需要加( 叶子结点数+1)/2调边...

这道题就是在做完Tarjan找作为叶子节点的双连通分量的个数k..然后答案就是(k+1)/2...

Program:

// POJ3352 加边做双连通图
#include<iostream>
#include<stack>
#include<math.h>
#define MAXN 5001
using namespace std;
struct pp
{
int x,y,next;
}line[MAXN*5];
int n,m,link[MAXN],i,p,low[MAXN],dfn[MAXN],tp[MAXN],numtp,sum[MAXN];
bool instack[MAXN],used[MAXN];
stack<int> mystack;
void trajin(int h)
{
int k;
instack[h]=true;
mystack.push(h);
low[h]=dfn[h]=++p;
k=link[h];
while (k)
{
if (!dfn[line[k].y])
{
trajin(line[k].y);
low[h]=min(low[h],low[line[k].y]);
}else
if (instack[line[k].y])
{
low[h]=min(low[h],dfn[line[k].y]);
}
k=line[k].next;
}
k=h;
if (low[k]==dfn[k])
{
numtp++;
do
{
k=mystack.top();
tp[k]=numtp;
mystack.pop();
instack[k]=false;
}while ( low[k]!=dfn[k]);
}
return;
}
void getanswer()
{
int i;
memset(instack,false,sizeof(instack));
while (!mystack.empty()) mystack.pop();
memset(tp,0,sizeof(tp));
memset(dfn,0,sizeof(dfn));
p=0; numtp=0;
for (i=1;i<=n;i++)
if (!dfn[i])
trajin(i);
for (i=1;i<=n;i++)
if (!tp[i])
{
printf("\n");
return;
}
memset(link,0,sizeof(link));
memset(sum,0,sizeof(sum));
for (i=1;i<=m;i++)
if (tp[line[i].x]!=tp[line[i].y])
{
p++;
sum[tp[line[i].x]]++;
}
for (i=1;i<=n;i++)
if (!sum[tp[i]])
printf("%d ",i);
printf("\n");
return;
}
int main()
{
while (~scanf("%d%",&n))
{
if (!n) break;
scanf("%d",&m);
memset(link,0,sizeof(link));
memset(line,0,sizeof(line));
for (i=1;i<=m;i++)
{
scanf("%d%d",&line[i].x,&line[i].y);
line[i].next=link[line[i].x];
link[line[i].x]=i;
}
getanswer();
}
return 0;
}




版权声明

本文仅代表作者观点,不代表博信信息网立场。

热门