POJ3177:利用Tarjan求无向图的边双连通分支
连通图去掉所有的桥(割边)之后,剩下的就是一块儿一块儿的边双连通分支了
那么这道题的描述是给定无向图G,问至少加入多少条边才能让原图成为一个双连通图
这个题的做法是利用Tarjan求出图中的所有桥,以桥为界限分出来的就是一个一个边的双连通分量
然后要做的是缩点,把所有的双连通分量缩成点,这样做了之后,会形成一棵树
那么答案要求至少要添加多少条边才能让原图成为双连通图,只要把树的叶子节点都连接起来就好了
我们最少要连的边=(叶子节点数+1)/2
这个题在初识的时候可以看成Tarjon强连通缩点和Tarjon求桥的一个结合
更深入的认识等以后总结的时候再说
int n,m,cnt,deep,bridge,sum,top,ans; int g[maxn],dfn[maxn],low[maxn],col[maxn],vis[maxn],st[maxn],deg[maxn]; struct Edge{int t,next;bool cut;}e[maxm];
col在这里面是双连通块儿,deg是点的度数,用来统计缩点图(这里是树)的叶子结点
inline int change(int x) { if(x%2==0) x--; else x++; return x; }
然后双向边加入的时候是正好一奇一偶,如果初始边的编号为0,的话,亦或1就好了,如果初始为1 就要这么处理一下子
然后是Tarjan,无向图版本的
void tarjan(int u,int fa) { dfn[u]=++deep;low[u]=deep; vis[u]=1; st[++top]=u; for(int tmp=g[u];tmp;tmp=e[tmp].next) { int v=e[tmp].t;if(v==fa) continue; if(!dfn[v]) { tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]) { bridge++; e[tmp].cut=true; int tmp1=change(tmp); e[tmp1].cut=true; } } else if(vis[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { col[u]=++sum;vis[u]=0; while(st[top]!=u) { col[st[top]]=sum; vis[st[top--]]=0; } top--; } }
然后是缩点和统计结果的过程
for(u=1;u<=n;u++) for(int tmp=g[u];tmp;tmp=e[tmp].next) if(e[tmp].cut) deg[col[u]]++; for(int i=1;i<=sum;i++) if(deg[i]==1) ans++; printf("%d ",(ans+1)/2);
给出完整实现:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 const int maxn=5005; 5 const int maxm=20005; 6 using namespace std; 7 int n,m,cnt,deep,bridge,sum,top,ans; 8 int g[maxn],dfn[maxn],low[maxn],col[maxn],vis[maxn],st[maxn],deg[maxn]; 9 struct Edge{int t,next;bool cut;}e[maxm]; 10 void addedge(int u,int v) 11 { 12 e[++cnt].t=v;e[cnt].next=g[u];e[cnt].cut=0; 13 g[u]=cnt; 14 } 15 inline int change(int x) 16 { 17 if(x%2==0) 18 x--; 19 else 20 x++; 21 return x; 22 } 23 void tarjan(int u,int fa) 24 { 25 dfn[u]=++deep;low[u]=deep; 26 vis[u]=1; 27 st[++top]=u; 28 for(int tmp=g[u];tmp;tmp=e[tmp].next) 29 { 30 int v=e[tmp].t;if(v==fa) continue; 31 if(!dfn[v]) 32 { 33 tarjan(v,u); 34 low[u]=min(low[u],low[v]); 35 if(low[v]>dfn[u]) 36 { 37 bridge++; 38 e[tmp].cut=true; 39 int tmp1=change(tmp); 40 e[tmp1].cut=true; 41 } 42 } 43 else if(vis[v]) low[u]=min(low[u],dfn[v]); 44 } 45 if(dfn[u]==low[u]) 46 { 47 col[u]=++sum;vis[u]=0; 48 while(st[top]!=u) 49 { 50 col[st[top]]=sum; 51 vis[st[top--]]=0; 52 } 53 top--; 54 } 55 } 56 void init() 57 { 58 cnt=deep=bridge=sum=top=ans=0; 59 memset(g,0,sizeof(g)); 60 memset(dfn,0,sizeof(dfn)); 61 memset(low,0,sizeof(low)); 62 memset(vis,0,sizeof(vis)); 63 memset(st,0,sizeof(st)); 64 memset(deg,0,sizeof(deg)); 65 memset(e,0,sizeof(e)); 66 } 67 int main() 68 { 69 int u,v; 70 while(scanf("%d%d",&n,&m)==2) 71 { 72 init(); 73 while(m--) 74 { 75 scanf("%d%d",&u,&v); 76 addedge(u,v),addedge(v,u); 77 } 78 tarjan(1,0); 79 for(u=1;u<=n;u++) 80 for(int tmp=g[u];tmp;tmp=e[tmp].next) 81 if(e[tmp].cut) deg[col[u]]++; 82 for(int i=1;i<=sum;i++) 83 if(deg[i]==1) ans++; 84 printf("%d ",(ans+1)/2); 85 } 86 return 0; 87 }