1. 程式人生 > >hdu 4612 Warm up【縮點+求樹的直徑+tarjan求割橋】

hdu 4612 Warm up【縮點+求樹的直徑+tarjan求割橋】

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=4612

Problem Description
  N planets are connected by M bidirectional channels that allow instant transportation. It's always possible to travel between any two planets through these channels.
  If we can isolate some planets from others by breaking only one channel , the channel is called a bridge of the transportation system.
People don't like to be isolated. So they ask what's the minimal number of bridges they can have if they decide to build a new channel.
  Note that there could be more than one channel between two planets.
 

Input
  The input contains multiple cases.
  Each case starts with two positive integers N and M , indicating the number of planets and the number of channels.
  (2<=N<=200000, 1<=M<=1000000)
  Next M lines each contains two positive integers A and B, indicating a channel between planet A and B in the system. Planets are numbered by 1..N.
  A line with two integers '0' terminates the input.
 

Output
  For each case, output the minimal number of bridges after building a new channel in a line.
 

Sample Input
4 4 1 2 1 3 1 4 2 3 0 0
 

Sample Output
0
 

Source
2013 Multi-University Training Contest 2

題意:給定一個無向連通圖加上一條邊後所得到的圖所含的橋的數目最少。

思路:可以先 tarjan 求出割橋的數量,縮點後求出樹的直徑,ans=割橋➖直徑;

dfs求樹的直徑:先從一個點進行 dfs,找到深度最大的點(即最長的樹枝),再從這個點進行 dfs,就可以找的樹的直徑;
(仔細想一下就懂了)

縮點:新建的圖中,點是原來圖的強連通分量,邊就是如果不是一個強連通分量中,就建邊;

#include<cstring>
#include<string>
#include<cstdio>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define inf 0x3f3f3f3f
#include<queue>
#include<set>
using namespace std;
typedef long long ll;
const int N=4e5+5;
const int M=2e6+5;

struct node
{
    int v,ne,f;// f來標記是否為割橋
}edge[M];

int head[N],dfn[N],low[N];
int belong[N];//該點存縮點
int vis[N];//標記是否在棧內
int e,top,ans,cut;
int n,m;
int deep[N];
stack<int>sta;//tarjan需要
vector<int>vec[N];//用來存縮點後的新圖

void init()
{
    memset(head,-1,sizeof(head));
    e=0;
    while(!sta.empty())
        sta.pop();
}

void add(int a,int b)
{
    edge[e].v=b;
    edge[e].ne=head[a];
    edge[e].f=0;
    head[a]=e++;
}

void tarjan(int now,int pre)
{
    sta.push(now);
    low[now]=dfn[now]=++top;
    vis[now]=1;
    bool flag=1;
    for(int i=head[now];i!=-1;i=edge[i].ne)
    {
        int v=edge[i].v;
        if(v==pre&&flag)//判斷是否為重邊
        {
            flag=0;
            continue;
        }
        if(!dfn[v])
        {
            tarjan(v,now);
            low[now]=min(low[now],low[v]);
            if(dfn[now]<low[v])
            {
                ans++;
                edge[i].f=1;
                edge[i^1].f=1;
            }
        }
        else if(vis[v])
            low[now]=min(dfn[v],low[now]);
    }
    if(low[now]==dfn[now])//縮點操作
    {
        cut++;
        int v;
        while(true)
        {
            v=sta.top();
            sta.pop();
            belong[v]=cut;
            vis[v]=0;
            if(v==now) break;
        }
    }
}

//
void dfs(int now)//dfs找樹的直徑
{
    for(int i=0;i<vec[now].size();i++)
    {
        int v=vec[now][i];
        if(deep[v]==-1)
        {
            deep[v]=deep[now]+1;
            dfs(v);
        }
    }
}

void solve()
{
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    ans=0;
    cut=0;
    top=0;
    tarjan(1,-1);
//==========樹的直徑==============   
    for(int i=1;i<=cut;i++)
        vec[i].clear();     
    for(int i=1;i<=n;i++)//將縮點後的點從新建圖
    {
        for(int j=head[i];j!=-1;j=edge[j].ne)
        {
            if(edge[j].f)
            {
                int v=edge[j].v;
                vec[belong[i]].push_back(belong[v]);
            }
        }
    }
    memset(deep,-1,sizeof(deep));
    int k=0,maxx=0;
    deep[1]=0;
    dfs(1);//先從 1 開始找最長樹枝
    for(int i=1;i<=cut;i++)
    {
        if(deep[i]>maxx)
        {
            k=i;
            maxx=deep[i];
        }
        deep[i]=-1;
    }//在從找的 k點找從k開始的最長樹枝(即樹的直徑)
    deep[k]=0;
    dfs(k);
    maxx=0;
    for(int i=1;i<=cut;i++)
    {
        if(deep[i]>maxx)
            maxx=deep[i];
    }
//================================
    printf("%d\n",ans-maxx);
}

int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        if(!n&&!m) break;
        init();
        int a,b;
        for(int i=0;i<m;i++)
        {
            scanf("%d %d",&a,&b);
            add(a,b);
            add(b,a);
        }
        solve();
    }
    return 0;
}