题目链接: https://vjudge.net/problem/POJ-3177
思路: 先根据边双连通分量把图缩点成为一棵树,再根据树的性质,由题意可知,只要把度为1的点两两相连即可(注意考虑重边)
#include<iostream> #include<map> #include<vector> using namespace std; bool bridge[5001][5001]; vector<int> edge[5001]; vector<int> rpg[5001]; int dfn[5001]; int low[5001]; int ans[5001]; int drgee[5001]; int n,m; int cnt,cntb; void tarjan(int u,int fa) { cnt++; dfn[u]=cnt; low[u]=cnt; int flag=1; for(int i=0;i<edge[u].size();i++) { int v=edge[u][i]; if(!dfn[v]) { tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]) bridge[u][v]=bridge[v][u]=1; } else if(fa!=v||flag>1)//重边的考虑 low[u]=min(low[u],dfn[v]); if(fa==v)//如果有重边 , 需要把它加入到连通块中 flag++; } } void edcc(int u) { ans[u]=cntb; for(int j=0;j<edge[u].size();j++) { int v=edge[u][j]; if(ans[v]||bridge[u][v]) continue; edcc(v); } } int main(void) { cin>>n>>m; for(int i=1;i<=m;i++) { int u,v; cin>>u>>v; edge[u].push_back(v); edge[v].push_back(u); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,0); for(int i=1;i<=n;i++) if(!ans[i]) {cntb++;edcc(i);} // for(int i=1;i<=n;i++) // cout<<ans[i]<<" "; // cout<<endl; int count=0; for(int i=1;i<=n;i++) { for(int j=0;j<edge[i].size();j++) { int t=edge[i][j]; if(ans[i]!=ans[t]) { drgee[ans[i]]++; drgee[ans[t]]++; rpg[ans[i]].push_back(ans[t]); rpg[ans[t]].push_back(ans[i]); } } } int p=0; for(int i=1;i<=cntb;i++) { if(drgee[i]==2) p++; } cout<<(p+1)/2<<endl; }