链接:http://poj.org/problem?id=3694
题意:有一个网络。问每添加一条边,整个新图中有多少条桥?
#include<cstdio>
#include<cstring>
#define MAXN 100005
using namespace std;
struct Edge
{
int to;
int next;
}edge[MAXN*4];
int n,m;
int ans,cnt,index;
int head[MAXN],dfn[MAXN],low[MAXN],farther[MAXN],set[MAXN];
//father[u]为当前u节点的父节点
//set数组用于并查集中节点的划分
void init()
{
index=ans=cnt=0;
for(int i=1;i<=n;i++)
{
set[i]=i;
dfn[i]=0;
head[i]=-1;
farther[i]=0;
}
}
void add(int u,int v)
{
edge[cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].to=u;
edge[cnt].next=head[v];
head[v]=cnt++;
}
int find(int x)
{
if(x!=set[x])
set[x]=find(set[x]);
return set[x];
}
int merge(int a,int b)
{
int x=find(a);
int y=find(b);
if(x==y)return 0;
else
set[y]=x;
return 1;
}
void tarjan(int u,int fa)
{
int i,v;
dfn[u]=low[u]=++index;
for(i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(v==fa)continue;
if(!dfn[v])
{
tarjan(v,u);
farther[v]=u; //设置当前节点v的父节点为u
if(low[u]>low[v])
low[u]=low[v];
if(dfn[u]<low[v]) //edge(u,v)为割边,ans++
ans++;
else
merge(u,v);//否则,属于一个连通分量,就合并,利用并查集缩点
}
else if(low[u]>dfn[v])
low[u]=dfn[v];
}
}
void lca(int u,int v)
{
while(u!=v)
{
while(dfn[u]>=dfn[v]&&u!=v)//点u的深度大于点v的深度
{
if(merge(u,farther[u]))//如果u和father[u]不在同一个连通分量,则桥减一,因为势必形成环
ans--;
u=farther[u]; //点u向上爬
}
while(dfn[v]>=dfn[u]&&u!=v)
{
if(merge(v,farther[v]))
ans--;
v=farther[v];
}
}
}
int main()
{
int q,i,CASE=0;
int a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(!n&&!m)return 0;
init();
for(i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
}
scanf("%d",&q);
tarjan(1,-1);
printf("Case %d:\n",++CASE);
for(i=0;i<q;i++)
{
scanf("%d%d",&a,&b);
lca(a,b);
printf("%d\n",ans);
}
}
}
再附上网上的另一种方法,应该说方法一样的,只是一个是直接通过标记桥来判断。
思路:求双连通分量,利用并查集缩点,形成一棵树,树边肯定都是桥,然后每对点x,y,找原图中x,y点对应的新图中的点,如果不是一个点,则向上找它们的LCA,因为它们之间连了一条边,所以这些点到它们的LCA之间的边都不是割边了,找LCA时,先将两点上升到同一层次,然后一起再向上找父亲节点,其间遇到桥就把桥的标记删除,并且答案减1。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define MAXN 100005
#define MAXM 555555
#define INF 1000000000
using namespace std;
struct Edge
{
int v, next;
}edge[MAXM];
int low[MAXN], dfn[MAXN], index, vis[MAXN];
int e, n, m, head[MAXN];
int cnt, bridge[MAXN], father[MAXN];
void init()
{
e = 0, index = 0, cnt = 0;
memset(vis, 0, sizeof(vis));
memset(dfn, 0, sizeof(dfn));
memset(bridge, 0, sizeof(bridge));
memset(head, -1, sizeof(head));
for(int i = 1; i <= n; i++) father[i] = i;
}
void insert(int x, int y)
{
edge[e].v = y;
edge[e].next = head[x];
head[x] = e++;
}
void tarjan(int u)
{
vis[u] = 1;
dfn[u] = low[u] = ++index;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(!vis[v])
{
father[v] = u;
tarjan(v);
low[u] = min(low[u], low[v]);
if(low[v] > dfn[u])
{
cnt++;
bridge[v] = 1;
}
}
else if(vis[v] == 1 && v != father[u]) low[u] = min(low[u], dfn[v]);
}
vis[u] = 2;
}
void LCA(int u, int v)
{
while(dfn[u] > dfn[v])
{
if(bridge[u]) cnt--, bridge[u] = 0;
u = father[u];
}
while(dfn[v] > dfn[u])
{
if(bridge[v]) cnt--, bridge[v] = 0;
v = father[v];
}
while(u != v)
{
if(bridge[u]) cnt--, bridge[u] = 0;
if(bridge[v]) cnt--, bridge[v] = 0;
u = father[u];
v = father[v];
}
}
void ask()
{
int q, u, v;
scanf("%d", &q);
while(q--)
{
scanf("%d%d", &u, &v);
LCA(u, v);
printf("%d\n", cnt);
}
printf("\n");
}
int main()
{
int cas = 0;
while(scanf("%d%d", &n, &m) != EOF)
{
if(n == 0 && m == 0) break;
printf("Case %d:\n", ++cas);
init();
int x, y;
while(m--)
{
scanf("%d%d", &x, &y);
insert(x, y);
insert(y, x);
}
tarjan(1);
ask();
}
return 0;
}
转自:http://blog.csdn.net/sdj222555/article/details/7211644
http://blog.csdn.net/zhengnanlee/article/details/22646327