题目链接:
http://poj.org/problem?id=3352
题目大意:
一个热带天堂岛上有N个旅游景点,任意2个旅游景点之间都有路径(并不一定直接相连)。为了使游客
往返更便捷,该旅游公司要求增加一些道路。在施工的时候,每次只能选择一条道路施工,在施工完
毕之前,除了该道路意外,其他道路依旧能够通行。因为施工道路禁止通行,这就导致了在施工期间
游客可能无法到达一些经典。
该公司为了保证在施工期间所有的旅游景点都能够向游客开放,该公司决定搭建一些临时桥梁,使得
无论在哪条道路施工,游客都能到达所有的旅游景点。那么问题来了:给你N个景点和M条双向边,
问:最少搭建几条临时桥梁,才能够使无论在哪条道路施工,都不会影响游客达到所有的旅游景点。
思路:
题目可以转变为:给你一个无向连通图,问至少添加几条边,才能变成双连通图。下边我们来考虑这
道题。当图中存在割边(桥)的时候,删掉它,显然构不成双连通图。割边的两端分属于两个边连通分
量。删除它原图就分成了两个边连通分量。所以必须在两个边连通分量之间再添加一条边(也就是题目
中说的临时桥梁,但不是刚才的割边),这两个边连通分量之间也成了双连通。那么,如果原图有多个
边连通分量,怎么添加临时边,才能使得任意两个边连通分量之间都成为双连通。
(1)找出原图的所有边连通分量。
(2)对原图进行缩点,将同属一个边连通分量的点看做一个点。
(3)求缩点后,最少添加几条双向边,使得全部点之间都能够直接到达。
关于第三步,参考下图来说明解决方法:
原图G缩点后如右图所示。若使得所有边连通分量之间都成为双连通。就得使右图中A、B、C、D之间
能够相互连接。可以明显看出右图再添加2条边就可以满足要求。添加(A、B),使得A、B、D相互双连
通。再添加(B、C)或(A、C)使得A、B、C、D全部相互双连通。
那么第三步求出缩点后所有度数为1的点个数。结果ans = (缩点后度数为1的点个数+1) / 2。
参考博文:http://blog.csdn.net/lyy289065406/article/details/6762370
AC代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; const int MAXN = 1100; const int MAXM = 5500; struct EdgeNode { int to; int next; }Edges[MAXM]; int Head[MAXN]; int dfn[MAXN],low[MAXN],Stack[MAXN],degree[MAXN]; int m,id,scc,lay,M,N; void AddEdges(int u,int v) { Edges[id].to = v; Edges[id].next = Head[u]; Head[u] = id++; } void TarjanBFS(int pos,int from) { Stack[m++] = pos; dfn[pos] = low[pos] = ++lay; for(int i = Head[pos]; i != -1; i = Edges[i].next) { if(Edges[i].to == from) continue; if( !dfn[Edges[i].to] ) { TarjanBFS(Edges[i].to,pos); low[pos] = min(low[pos],low[Edges[i].to]); // if( dfn[pos] < low[Edges[i].to] ) // { // while(Stack[m--] != Edges[i].to); // } } else low[pos] = min(low[pos],low[Edges[i].to]); } } int main() { int u,v; while(~scanf("%d%d",&N,&M)) { memset(Head,-1,sizeof(Head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(degree,0,sizeof(degree)); m = lay = scc = id = 0; for(int i = 0; i < M; ++i) { scanf("%d%d",&u,&v); AddEdges(u,v); AddEdges(v,u); } for(int i = 1; i <= N; ++i) if( !dfn[i] ) TarjanBFS(1,-1); for(int i = 1; i <= N; ++i) { for(int j = Head[i]; j != -1; j = Edges[j].next) { if(low[i] != low[Edges[j].to]) degree[low[i]]++; } } int ans = 0; for(int i = 1; i <= N; ++i) if(degree[i] == 1) ans++; printf("%d\n",(ans+1)/2); } return 0; }