poj3177重修道路——边双连通分量缩点
题目:http://poj.org/problem?id=3177
找桥,缩点,总之都是板子;
对于每个叶子,互相连一条边即可;若最后剩下一个,则去和根节点连边;
所以叶子节点数+1再/2即答案。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int n,m,ct=1,head[5005],reg[5005],c[5005],dc,dfn[5005],low[5005],tim,ans; bool bri[10005]; struct N{ int to,next; N(int t=0,int n=0):to(t),next(n) {} }edge[10005],dcc[10005]; void tarjan(int x,int e) { tim++; dfn[x]=tim; low[x]=tim; for(int i=head[x];i;i=edge[i].next) { int u=edge[i].to; // if(i==(e^1))continue; if(!dfn[u]) { tarjan(u,i); low[x]=min(low[x],low[u]); if(low[u]>dfn[x])bri[i]=1,bri[i^1]=1;//!!! } else if(i!=(e^1)) low[x]=min(low[x],dfn[u]); } } void dfs(int x) { c[x]=dc; for(int i=head[x];i;i=edge[i].next) { int u=edge[i].to; if(!c[u]&&!bri[i]) dfs(u); } } int main() { while(scanf("%d%d",&n,&m)==2) { memset(head,0,sizeof head); memset(reg,0,sizeof reg); memset(c,0,sizeof c); memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); memset(bri,0,sizeof bri); ct=1;ans=0;dc=0;tim=0; for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); edge[++ct]=N(y,head[x]);head[x]=ct; edge[++ct]=N(x,head[y]);head[y]=ct; } tarjan(1,0); for(int i=1;i<=n;i++) { if(c[i])continue; dc++; dfs(i); } for(int i=2;i<=ct;i+=2) { int u=edge[i].to,v=edge[i^1].to; if(c[u]==c[v])continue; reg[c[u]]++;reg[c[v]]++; } for(int i=1;i<=dc;i++) if(reg[i]==1)ans++; printf("%d\n",(ans+1)/2); } return 0; }