题目链接:http://poj.org/problem?id=3352
题目要求求出无向图中最少需要多少边能够使得该图边双连通。
在图G中,如果任意两个点之间有两条边不重复的路径,称为“边双连通”,去掉任何一条边都是其他边仍然是连通的,也就是说边双连通图中没有割边。
算法设计是:运用tarjan+缩点。对于每一个边双连通分量,我们都可以把它视作一个点,因为low值相同的点处在同一个边双连通分量中,可以简单地思考一下,(u,v)之间有两条可达的路径,dfs一定可以从一条路开始搜索并且从另一条路回去。而边双连通分量最初开始搜索的一个点的编号就是这个边双连通分量的low值,因为假设这个点的low值更加靠前的话,他与之前的结点一定是边双连通的关系,有一条边搜索到这个点,这个点在之后还有一条不重合的路径能到达先前那个结点,与边双连通分量的最大性产生矛盾,其实我们也可以把那个点收纳进这个边连通分量,把它作为该连通分量第一个进入dfs树的结点。故有边双连通分量中的点low值相同。
最终将这些点变成一个新的点图,这个点图中最大的边双连通分量就是一个点,所以我们只要看在这张图中需要加多少条边可以使得它边双连通就行。可以证明使得缩点图边双连通加上的边数是 (1+度数为1的点的数量)/2。由于这些缩点的low值都是不同的,所以我们可以用low值作为degree(点的度数)的索引。计算度数的时候时候扫描所有的点,只要跟一个low值不同的点有边,那么该low值(缩点)一定有一个外界的度(可看成入度),最后统计最终的只有一个度的连通分量数量即可。
代码如下:
1 #include<cstring> 2 #include<vector> 3 #include<stdio.h> 4 using namespace std; 5 const int maxn =1005; 6 int n,m,low[maxn],dfn; 7 vector<int>G[maxn]; 8 void dfs(int u,int fa)//先用dfs处理处每个点的low值,以便相同的low值合并 9 {//处理low值时不需要处理dfn数组 10 low[u]=++dfn; 11 for(int i=0;i<G[u].size();i++) 12 { 13 int v=G[u][i]; 14 if(v==fa)continue;//保证dfs 树的前向性 15 if(!low[v]) 16 dfs(v,u); 17 low[u]=min(low[u],low[v]); 18 } 19 } 20 int tarjan() 21 { 22 int degree[maxn]; 23 memset(degree,0,sizeof(degree)); 24 for(int i=1;i<=n;i++) 25 { 26 for(int j=0;j<G[i].size();j++) 27 { 28 if(low[i]!=low[G[i][j]]) 29 degree[low[i]]++; 30 } 31 } 32 int res=0; 33 for(int i=1;i<=n;i++) 34 { 35 if(degree[i]==1)res++; 36 } 37 return res; 38 } 39 int main() 40 { 41 while(~scanf("%d%d",&n,&m)) 42 { 43 int x,y; 44 memset(low,0,sizeof(low)); 45 for(int i=0;i<=n;i++)G[i].clear(); 46 while(m--) 47 { 48 scanf("%d%d",&x,&y); 49 G[x].push_back(y); 50 G[y].push_back(x); 51 } 52 dfn=0;//每次dfs之前设置dfs树的编号从1开始 53 dfs(1,-1); 54 int ans=tarjan(); 55 printf("%d\n",(ans+1)/2); 56 } 57 return 0; 58 }
原文地址:https://www.cnblogs.com/randy-lo/p/12585226.html
时间: 2024-11-07 16:59:52