poj3117,题目大意增加几条边,将图变为双连通。
分析:无向图跑tarjan,找出双连通,也就是环,然后缩点。缩点后重新处理图,统计叶子节点数leaf,总数即为(leaf+1)/2。
注意坑点:本题的输入数据有重边,但是重边只能作为一条边,要去重。
/*
图的割边
即在一个无向连通图中,如果删除某条边后,图不再连通,则成为割边。
求割边时,只需要将求割点的算法修改一个符号即可。只需要将low[v]>=num[u]改为low[v]>num[u]。
因为low[v]>=num[u]代表的是v不可能在不经过父亲u而回到祖先。如果low[v]=num[u],表示还可以回到父亲。而low[v]>num[u]则表示连父亲都回不到了。倘若顶点v不能回到祖先,也没有另外一条路能回到父亲,那么u-v就是割边。
*/
#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
const int maxn=5009;
struct node {
int to,nxt;
} e[4*maxn];
int n,m,cnt=0,index=0,head[maxn],dfn[maxn],low[maxn],du[maxn],map[maxn][maxn];
void add(int u,int v) {
e[cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt++;
}
void tarjan(int u,int fa) {
dfn[u]=low[u]=++index;
for(int i=head[u]; i>-1; i=e[i].nxt) {
int v=e[i].to;
if(!dfn[v]) {
tarjan(v,u);
low[u]=min(low[u],low[v]);
//if(low[v]>dfn[u])iscut[i]=iscut[i^1]=1; //割边
} else if(v!=fa)
low[u]=min(low[u],dfn[v]);
}
}
int main() {
cin>>n>>m;
int u,v;
memset(head,-1,sizeof(head));
for(int i=1; i<=m; i++) {
scanf("%d%d",&u,&v);
if(!map[u][v]) {
add(u,v);
add(v,u);
map[u][v]=1;
}
}
for(int i=1; i<=n; i++)
if(!dfn[i])tarjan(i,-1);
//for(int i=1;i<=n;i++)
// cout<<i<<" dfn="<<dfn[i]<<" low"<<low[i]<<endl;
//tarjan(1,-1);
for(int i=1; i<=n; i++) //缩点统计
for(int j=head[i]; j>-1; j=e[j].nxt) {
int v=e[j].to;
if(low[v]!=low[i])du[low[i]]++;
}
int leaf=0;
for(int i=1; i<=n; i++)
if(du[i]==1)leaf++;
printf("%d\n",(leaf+1)/2);
}