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

本文详细介绍了无向图中桥与割点的概念,并阐述了由Tarjan发明的算法如何寻找这些元素。进一步探讨了如何使用此算法解决特定问题,例如将无向图转变为双连通图所需的最少边数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

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


(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很像...注意几个不同...

1、没有栈..所以判断时先是看点有没有访问过...else的时候就直接更新Low...不需要再来个判断instack....

2、DFS时不能从 a 递归到了 b..b又马上从a来更新...所以要多加一个notpre..代表递归b时是从哪个点进去的...防止这种情况..

3、Low相等的点在无向图中就是在一个双连通图中...这个比有向图的方便..有向图还需要用栈来维护..通过判断退栈来判断强连通分量..

如果要求桥和割点的话..做完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; }



 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值