给定一个无向图,问至少加几条边使得该图双向联通,即每两个点双向可达。
自己动手画几个图会发现缩点之后(叶子节点数+1)/2就是答案。
/****************************
* author:crazy_石头
* date:2014/01/18
* algorithm:tarjan
* Pro:POJ 3177
***************************/
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <string>
#include <queue>
#include <vector>
using namespace std;
#define INF 1<<29
#define eps 1e-8
#define A system("pause")
#define rep(i,h,n) for(int i=(h);i<=(n);i++)
#define ms(a,b) memset((a),(b),sizeof(a))
const int maxn=3000+10;
const int maxm=3000+10;
struct edge
{
int from,to,next;
}e[maxm];
int dfn[maxn],low[maxn],belong[maxn],stack[maxn],out[maxn],head[maxn],scc,top,index,n,m,cnt;
bool instack[maxn],hash[maxn][maxn];
inline void addedge(int u,int v)
{
e[cnt].from=u;
e[cnt].to=v;
e[cnt].next=head[u];
hash[u][v]=hash[v][u]=true;
head[u]=cnt++;
}
inline void tarjan(int u,int f)
{
int v;
dfn[u]=low[u]=++index;
stack[top++]=u,instack[u]=1;
for(int i=head[u];~i;i=e[i].next)
{
v=e[i].to;
if(v==f) continue;
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
}else if(instack[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
scc++;
do
{
v=stack[--top];
instack[v]=0;
belong[v]=scc;
}while(u!=v);
}
}
inline void init()
{
int u,v;
scc=index=top=cnt=0;
ms(dfn,0),ms(instack,0),ms(head,-1),ms(low,0),ms(belong,0);
rep(i,1,m)
{
scanf("%d%d",&u,&v);
if(hash[u][v]) continue;
addedge(u,v);
addedge(v,u);
}
rep(i,1,n) if(!dfn[i]) tarjan(i,i);
}
inline void solve()
{
int i,v,ans=0;;
ms(out,0);
rep(i,0,cnt-1)
{
int u=e[i].from,v=e[i].to;
u=belong[u],v=belong[v];
if(u!=v) out[u]++;//记录出度;
}
rep(i,1,scc) if(out[i]==1) ans++;//记录叶子结点个数;
int ret=(ans+1)/2;
printf("%d\n",ret);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
solve();//缩点后重新构图;
}
return 0;
}