题目链接:http://poj.org/problem?id=3177
题意:给出一个n个点,m条边的连通图,问至少加几条边使两两点可以至少两条路到达。
思路:题目就是要将一个有桥的连通图变成双连通图。
把双连通子图缩点,形成一颗树。假设树的叶子节点有leaf个,至少要加的边数就是(leaf+1)/2。
为什么是(leaf+1)/2?只要两两叶子成对相连即可。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=5500; const int maxm=20050; struct node{ int u,v,w,nxt,cut; }e[maxm]; int h[maxn],dfn[maxn],low[maxn],st[maxn],vis[maxn]; int du[maxn],belong[maxn],num,cnt,tot,top,ans,n,m; void add(int u,int v) { e[cnt].u=u; e[cnt].v=v; e[cnt].cut=0; e[cnt].nxt=h[u]; h[u]=cnt++; } void tarjan(int u,int pre) { dfn[u]=low[u]=++tot; vis[u]=1; st[++top]=u; int pre_cnt=0; for(int i=h[u];i!=-1;i=e[i].nxt) { int v=e[i].v; if(v==pre&&pre_cnt==0) { pre_cnt++; continue; } if(!dfn[v]) { tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>dfn[u])//桥 { e[i].cut=1; e[i^1].cut=1; } } else if(vis[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { int t; num++; do{ t=st[top--]; vis[t]=0; belong[t]=num; }while(t!=u); } } int main() { int u,v; cnt=top=tot=ans=0; memset(h,-1,sizeof(h)); memset(du,0,sizeof(du)); memset(dfn,0,sizeof(dfn)); memset(vis,0,sizeof(vis)); scanf("%d%d",&n,&m); for(int i=0;i<m;i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } tarjan(1,0); for(int i=1;i<=n;i++) { for(int j=h[i];j!=-1;j=e[j].nxt) { if(e[j].cut) du[belong[i]]++; } } for(int i=1;i<=num;i++) { //cout<<i<<' '<<du[i]<<endl; if(du[i]==1) ans++; } printf("%d ",(ans+1)/2); return 0; }