ZOJ 2588 求割边问题

题目链接:http://vjudge.net/problem/viewProblem.action?id=14877

题目大意:

要尽可能多的烧毁桥,另外还要保证图的连通性,问哪些桥是绝对不能烧毁的

 

我们很容易看出不能烧毁的是必然是作为割边存在的桥。

求割边,我们用Tarjan算法,这与求割点有点小区别在与,对于(u,v)的点low[v]>=dfn[u]时就表示u为割点,而low[v]>dfn[u]时才能说明(u,v)是一条割边

 

因为这里要求出割边的序号,所以在写边的结构体时,用id代表桥的序号,我们每次得到a,b总会添加两条边a->b和b->a,因为这是无向图,所以这两条边公用一个id

另外要注意的是这道题目允许两个地点有多条边出现,所以我们需要用一个tag标志位来注明是否有重边

oid addPath(int a,int b,int c)
{
    int i;
    for(i=first[a];i!=-1;i=path[i].next)
        if(path[i].y==b) break;
    if(i!=-1)//说明是重边
        path[i].tag=1;
    else{
        path[k].y=b,path[k].tag=0,path[k].next=first[a],path[k].id=c; first[a]=k;
        k++;
    }
}

每次深度搜索一个节点,不断更新上面的low值和dfn值,并找到low[v]>dfn[u]的边并将它们保存到bridge数组中,nbridge用来统计桥的数量

void dfs(int u,int fa)
{
    visit[u]=1,dfn[u]=low[u]=tmpdfn++;
    for(int i=first[u];i!=-1;i=path[i].next){
        int j=path[i].y;

        if(!visit[j]){
            dfs(j,u);
            low[u]=min(low[j],low[u]);
            if(low[j]>dfn[u]&&!path[i].tag)
                bridge[++nbridge]=path[i].id;
        }
        else{
            if(j!=fa) low[u]=min(low[u],dfn[j]);//j已被访问且不是父亲节点,说明可以形成一条回边
        }
    }
}

总代码如下:

#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

using namespace std;

#define N 10005

#define M 100005

int tmpdfn,k,nbridge,bridge[M],visit[N],dfn[N],low[N];

int first[N];

struct Path{

    int y,tag,id;

    int next;

}path[2*M];



void addPath(int a,int b,int c)

{

    int i;

    for(i=first[a];i!=-1;i=path[i].next)

        if(path[i].y==b) break;

    if(i!=-1)//说明是重边

        path[i].tag=1;

    else{

        path[k].y=b,path[k].tag=0,path[k].next=first[a],path[k].id=c;

        first[a]=k;

        k++;

    }

}



void dfs(int u,int fa)

{

    visit[u]=1,dfn[u]=low[u]=tmpdfn++;

    for(int i=first[u];i!=-1;i=path[i].next){

        int j=path[i].y;



        if(!visit[j]){

            dfs(j,u);

            low[u]=min(low[j],low[u]);

            if(low[j]>dfn[u]&&!path[i].tag)

                bridge[++nbridge]=path[i].id;

        }

        else{

            if(j!=fa) low[u]=min(low[u],dfn[j]);//j已被访问且不是父亲节点,说明可以形成一条回边

        }

    }

}

int main()

{

    int T,n,m,x,y;

    scanf("%d",&T);



    while(T--){

        scanf("%d%d",&n,&m);

        k=0,nbridge=0,tmpdfn=1;

        memset(first,-1,sizeof(first));

        memset(visit,0,sizeof(visit));

        memset(bridge,0,sizeof(bridge));

        for(int i=1;i<=m;i++)

        {

            scanf("%d%d",&x,&y);

            addPath(x,y,i);

            addPath(y,x,i);

        }

        dfs(1,0);

        printf("%d\n",nbridge);

        sort(bridge+1,bridge+nbridge+1);

        for(int i=1;i<nbridge;i++) printf("%d ",bridge[i]);

        if(nbridge>0) printf("%d\n",bridge[nbridge]);

        if(T>0) printf("\n");

    }

    return 0;

}
View Code

 

你可能感兴趣的:(ZOJ)