hdu 4612 Warm up(无向图Tarjan+树的直径)

题意:有N个点,M条边(有重边)的无向图,这样图中会可能有桥,问加一条边后,使桥最少,求该桥树。

思路:这个标准想法很好想到,缩点后,求出图中的桥的个数,然后重建图必为树,求出树的最长直径,在该直径的两端点连一边,则图中的桥会最少。
从这题中学到两点,所以写一下解题报告。
1.官方说judge的栈小,得手动增栈 #pragma comment(linker,"/STACK:102400000,102400000") 以前没见过,算是学习了。
2.对改正了对Tarjan算法的一个错误理解,以前看某人博客说,无向图中,Tarjan后low值相等的点属于同一块,以前这样判断过,也过了挺多题。但跟别人讨论后发现是错的。。。。。

3.以前没用Tarjan写过无向图,不懂正向边访问过,标志反向边,这次学习了。


 

//937MS    41304K

#pragma comment(linker, "/STACK:102400000,102400000")

#include

#include

const int VM = 200005;

const int EM = 1000005;



struct Edeg

{

    int to,nxt,vis;

}edge[EM<<1],tree[EM<<1];



int head[VM],vis[VM],thead[VM];

int dfn[VM],low[VM],stack[VM],belong[VM];

int ep,bridge,son,maxn,src,n,cnt,scc,top;



int max (int a,int b)

{

    return a > b ? a : b;

}

int min(int a ,int b)

{

    return a > b ? b : a;

}

void addedge (int cu,int cv)

{

    edge[ep].to = cv;

    edge[ep].vis = 0;

    edge[ep].nxt = head[cu];

    head[cu] = ep ++;

    edge[ep].to = cu;

    edge[ep].vis = 0;

    edge[ep].nxt = head[cv];

    head[cv] = ep ++;

}

void Buildtree(int cu,int cv)

{

    tree[son].to = cv;

    tree[son].nxt = thead[cu];

    thead[cu] = son ++;

}

void Tarjan (int u)

{

    int v;

    vis[u] = 1;

    dfn[u] = low[u] = ++cnt;

    stack[top++] = u;

    for (int i = head[u];i != -1;i = edge[i].nxt)

    {

        v = edge[i].to;

        if (edge[i].vis) continue; //

        edge[i].vis = edge[i^1].vis = 1; //正向边访问过了,反向边得标志,否则两点会成一块。

        if (vis[v] == 1)

            low[u] = min(low[u],dfn[v]);

        if (!vis[v])

        {

            Tarjan (v);

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

            if (low[v] > dfn[u])

                bridge ++;

        }

    }

    if (dfn[u] == low[u])

    {

        ++scc;

        do{

            v = stack[--top];

            vis[v] = 0;

            belong[v] = scc;

        }while (u != v);

    }

}

void BFS(int u)

{

    int que[VM+100];

    int front ,rear,i,v;

    front = rear = 0;

    memset (vis,0,sizeof(vis));

    que[rear++] = u;

    vis[u] = 1;

    while (front != rear)

    {

        u = que[front ++];

        front = front % (n+1);

        for (i = thead[u];i != -1;i = tree[i].nxt)

        {

            v = tree[i].to;

            if (vis[v]) continue;

            vis[v] = 1;

            que[rear++] = v;

            rear = rear%(n+1);

        }

    }

    src = que[--rear];//求出其中一个端点

}

void DFS (int u,int dep)

{

    maxn = max (maxn,dep);

    vis[u] = 1;

    for (int i = thead[u]; i != -1; i = tree[i].nxt)

    {

        int v = tree[i].to;

        if (!vis[v])

            DFS (v,dep+1);

    }

}

void solve()

{

    int u,v;

    memset (vis,0,sizeof(vis));

    cnt = bridge = scc = top = 0;

    Tarjan (1);

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

    son = 0;

    for (u = 1;u <= n;u ++)              //重构图

        for (int i = head[u];i != -1;i = edge[i].nxt)

        {

            v = edge[i].to;

            if (belong[u]!=belong[v])

            {

                Buildtree (belong[u],belong[v]);

                Buildtree (belong[v],belong[u]);

            }

        }

    maxn = 0;    //最长直径

    BFS(1);      //求树直径的一个端点

    memset (vis,0,sizeof(vis));

    DFS(src,0); //求树的最长直径

    printf ("%d\n",bridge-maxn);

}



int main ()

{

    #ifdef LOCAL

        freopen ("in.txt","r",stdin);

    #endif

    int m,u,v;

    while (~scanf ("%d%d",&n,&m))

    {

        if (n == 0&&m == 0)

            break;

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

        ep = 0;

        while (m --)

        {

            scanf ("%d%d",&u,&v);

            addedge (u,v);

        }

        solve();

    }

    return 0;

}


 

 

你可能感兴趣的:(tar)