題目來源:http://poj.org/problem?id=3352
引用一下解題思路:http://blog.csdn.net/geniusluzh/article/details/6619575
這道題的意思是說,給你一個無向圖,然後問你至少需要添加幾條邊,可以使整個圖變成邊雙連通分量,也就是說任意兩點至少有兩條路可以互相連通。我們這樣考慮這個問題,屬於同一個邊雙連通分量的任意點是至少有兩條通路是可以互相可達的,因此我們可以將一個邊雙連通分量縮成一個點。然後考慮不在邊雙連通分量中的點,通過縮點後形成的是一棵樹。對於一個樹型的無向圖,需要添加(度爲1的點的個數+1)/2條邊使得圖成爲雙連通的。這樣問題就是變成縮點之後,求圖中度爲1的點的個數了。
圖是無向圖,跑Tarjan的時候把無向圖當成有向圖跑就行了(就是不走回頭路)
做縮點的題的時候一個比較容易犯的馬虎錯是把原圖的點和新圖的點混淆,所以類似degree[bel[ver[i]]]寫成degree[ver[i]]的問題一定要注意。
另外寫鄰接表時有時會把當前結點和子節點混淆(就是把ver[i]寫成了now),這也要注意。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2000,M=2000;
int dfn[N],low[N],t=0;
int head[N],nxt[M<<1],ver[M<<1],tot=1;
bool danger[M<<1];
int degree[N],bel[N],scc=0,leaf=0;
int n,m;int col[N];
void add (int u,int v) {
ver[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}
void Tarjan (int x,int fa) {
dfn[x]=low[x]=++t;col[x]=-1;
for (int i=head[x];i;i=nxt[i]) {
if (ver[i]==fa) continue;
if (!dfn[ver[i]]) {
Tarjan(ver[i],x);
low[x]=min(low[x],low[ver[i]]);
if (dfn[x]<low[ver[i]])
danger[i]=danger[i^1]=1;//not node
}
else if (col[ver[i]]==-1) low[x]=min(low[x],dfn[ver[i]]);
}
col[x]=1;
}
void dfs (int x) {
bel[x]=scc;
for (int i=head[x];i;i=nxt[i])
if (!bel[ver[i]]&&!danger[i]) dfs(ver[i]);//bel[ver[i]] instead of bel[x],this is important
}
int solve () {
for (int i=1;i<=n;i++) {
if (bel[i]) continue;
++scc;dfs(i);
}
for (int u=1;u<=n;u++) {
for (int i=head[u];i;i=nxt[i])
if (bel[u]!=bel[ver[i]]) ++degree[bel[ver[i]]];
}
for (int i=1;i<=scc;i++) {
if (degree[i]==1) ++leaf;
}
return ((leaf+1)>>1);
}
int main () {
int u,v;
while (scanf("%d %d",&n,&m)!=EOF) {
memset(head,0,sizeof(head));
memset(degree,0,sizeof(degree));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(danger,0,sizeof(danger));
memset(bel,0,sizeof(bel));
memset(col,0,sizeof(col));
t=0,tot=1,scc=0,leaf=0;
for (int i=1;i<=m;i++) {
scanf("%d%d",&u,&v);
add(u,v); add(v,u);
}
for (int i=1;i<=n;i++) if (!dfn[i]) Tarjan(i,0);
printf("%d\n",solve());
}
return 0;
}