题目传送门:POJ-3177
题目大意:
给你一张图,然后问你如果在这张图上再加上一条边,那么这个图上的桥的数量最少是多少。
思路:
我们可以先用Tarjan缩点,把这张图缩成一棵树,然后这树上的所有边都是这个图上是桥,那么我们可以找到这个树的最长的一条路,首尾连起来,让它成为一个环,那么减去的桥数一点是最多的,树上最长的一条路就是树的直径,也就是说这道题的答案就是先Tarjan求桥数然后减去新建的树的树直径。
AC
//#include<bits/stdc++.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<math.h>
#include<map>
using namespace std;
#define LL long long
const double PI=acos(-1.0);
const double eps=1e-8;
const int N=1e4+10;
struct zxc
{
int to,net,id;
} e[10005<<1];
int tot=1;
int head[N];
int vis[N];
int dfn[N],low[N];
int cut[N];
int block[N];
int dus[N];
int n,m,ti,sum;
int root;
int ans=0;
stack<int>q;
void add(int u,int v,int id)
{
e[tot]= {v,head[u],id};
head[u]=tot++;
e[tot]= {u,head[v],id};
head[v]=tot++;
//printf("建边:%d-%d\n",u,v);
}
void init()
{
tot=1;
ti=1;
root=1;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(head,-1,sizeof(head));
//memset(cut,0,sizeof(cut));
memset(vis,0,sizeof(vis));
memset(block,0,sizeof(block));
memset(dus,0,sizeof(dus));
sum=0;
while(!q.empty())
{
q.pop();
}
}
void tarjan(int x,int fa)
{
dfn[x]=low[x]=ti++;
vis[x]=1;
q.push(x);
int cnt=0;
for(int i=head[x]; i!=-1; i=e[i].net)
{
int y=e[i].to;
//printf("%d %d##\n",x,y);
if(e[i].id==fa)
{
continue;
}
if(!dfn[y])
{
tarjan(y,e[i].id);
low[x]=min(low[x],low[y]);
// if(low[y]>dfn[x])
// {
// ans++;
// }
}
else if(vis[y])
{
low[x]=min(low[x],dfn[y]);
}
}
int y;
if(dfn[x]==low[x])
{
sum++;
do
{
y=q.top();
q.pop();
block[y]=sum;
vis[y]=0;
}while(y!=x);
}
}
void solve()
{
ans=0;
for(int i=1;i<=n;i++)
{
if(!dfn[i])
{
tarjan(i,-1);
}
}
int y;
for(int i=1;i<=n;i++)
{
for(int j=head[i];j!=-1;j=e[j].net)
{
y=e[j].to;
//printf("%d %d****\n",i,y);
if(block[i]==block[y])
{
continue;
}
dus[block[i]]++;
dus[block[y]]++;
}
}
for(int i=1;i<=sum;i++)
{
if(dus[i]/2==1)
{
ans++;
}
}
printf("%d\n",(ans+1)/2);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
int x,y;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y,i);
}
solve();
}
return 0;
}