poj3694 Network

链接: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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值