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;
}

猜你喜欢

转载自blog.csdn.net/qq_41984014/article/details/84036190