POJ 3352 Road Construction (双连通缩点)
题目大意:给定一个无向连通图,求至少需添加几条边,使得原图双连通(不存在桥)。
分析:用tarjan算法找桥,将所有不是桥的边的端点用并查集合并,这题以前写过,至于为什么可以用并查集来合并,可以参考以前那篇博客。
需要注意的是,数据中有重边,在判桥时要注意。可以用一个矩阵存储边的数目,若某边数目大于1,则一定不是桥。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> #include <string.h> #define MIN(a,b) ((a)<(b)?(a):(b)) #define N 1001 #define M 2002 int n,m,e; int g[N][N]; int first[N],next[M],v[M]; int dfn[N],low[N],id[N],cnt; int p[N]; int d[N]; void make_set() { for(int i=1;i<=n;i++) p[i]=i; } int find_set(int i) { if(i^p[i]) p[i]=find_set(p[i]); return p[i]; } void union_set(int i,int j) { i=find_set(i),j=find_set(j); if(i^j) p[j]=i; } void init() { e=0; memset(first,-1,sizeof(int)*(n+1)); cnt=0; memset(dfn,0,sizeof(int)*(n+1)); for(int i=1;i<=n;i++) memset(g[i],0,sizeof(int)*(n+1)); } void insert(int a,int b) { v[e]=b; next[e]=first[a]; first[a]=e++; g[a][b]++; } void dfs(int a,int fa) { int i,b; dfn[a]=low[a]=++cnt; for(i=first[a];i!=-1;i=next[i]) { b=v[i]; if(dfn[b] && b!=fa) low[a]=MIN(low[a],dfn[b]); else if(!dfn[b]) { dfs(b,a); low[a]=MIN(low[b],low[a]); if(low[b]<=dfn[a] || g[a][b]>1) { union_set(a,b); } } } } void solve() { int a,b,i,pi,k=0; dfs(1,0); memset(id,-1,sizeof(id)); for(i=1;i<=n;i++) { pi=find_set(i); if(id[pi]==-1) id[pi]=k++; } memset(d,0,sizeof(d)); for(a=1;a<=n;a++) { for(i=first[a];i!=-1;i=next[i]) { b=v[i]; if(find_set(a)^find_set(b)) d[id[p[a]]]++,d[id[p[b]]]++; } } int ans=0; for(i=0;i<k;i++) if(d[i]==2) ans++; printf("%d\n",(ans+1)/2); } int main() { int a,b; while(~scanf("%d%d",&n,&m)) { init(); make_set(); while(m--) { scanf("%d%d",&a,&b); insert(a,b); insert(b,a); } solve(); } return 0; }