題目大意:給一個連通圖,求最少加多少邊使它變成一個點聯通份量。ios
先找割邊,而後把沒有橋的點雙連通份量縮成一個連通份量。
git
這些連通份量按原來的關係連在一塊兒就是一顆樹。spa
把樹變成一個點雙聯圖圖須要加(葉節點數+1)/2個邊。code
問題是怎麼求點雙連通份量。blog
若是一個點的dfn=low,說明目前棧中的元素都須要彈出,成爲一個點雙連通份量。get
把他們記錄到結構體裏面就行了string
#include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> #include <stack> #define in(a) a=read() #define MAXN 10010 #define REP(i,k,n) for(int i=k;i<=n;i++) using namespace std; inline int read(){ int x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } int n,m,ans; int total=0,head[MAXN],nxt[MAXN<<1],to[MAXN<<1];//ÁÚ½Ó±í int ind,dfn[MAXN],low[MAXN],vis[MAXN<<1];//¸î±ß int num,bel[MAXN];//Ëõµã int du[MAXN]; stack <int> S; inline void adl(int a,int b){ total++; to[total]=b; nxt[total]=head[a]; head[a]=total; return ; } inline void tarjan(int u){ S.push(u); dfn[u]=low[u]=++ind; for(int e=head[u];e;e=nxt[e]){ if(e%2 && vis[e+1]) continue; if(!(e%2) && vis[e-1]) continue; if(vis[e]) continue; vis[e]=1; if(!dfn[to[e]]){ tarjan(to[e]); low[u]=min(low[u],low[to[e]]); } else low[u]=min(low[u],dfn[to[e]]); } if(low[u]==dfn[u]){ num++; int v; do{ v=S.top(); bel[v]=num; S.pop(); }while(u!=v); } return ; } int main(){ in(n),in(m); int a,b; REP(i,1,m) in(a),in(b),adl(a,b),adl(b,a); tarjan(1); REP(u,1,n) for(int e=head[u];e;e=nxt[e]) if(bel[u]!=bel[to[e]]) du[bel[u]]++,du[bel[to[e]]]++; REP(i,1,num) if((du[i]/2)==1) ans++; cout<<(ans+1)/2; return 0; }