zoukankan      html  css  js  c++  java
  • HDU4612Warm up 边双连通 Tarjan缩点

     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



    要求:树的直径+缩点

    和上一道题纠结了好久,到底怎么加入一个块。

    首先要明确边双连通分量和点双连通分量的区别与联系

    1.二者都是基于无向图

    2.边双连通分量是删边后还连通,而后者是删点

    3.点双连通分量一定是边双连通分量(除两点一线的特殊情况),反之不一定,见HDU3749

    4.点双连通分量可以有公共点,而边双连通分量不能有公共边

         最后补充:

                   点的双连通存桥(边),每访问一条边操作一次。 

                边的双连通存割点(点),访问完所有边后操作。

    此题是边双连通,所以属于后者。yeah~~·

    #pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<vector>
    using namespace std;
    const int maxn=200010;
    const int maxm=2000010;
    int Laxt[maxn],Next[maxm],To[maxm],cnt,vis[maxn];
    int dfn[maxn],low[maxn];
    int times,ans,cut_cnt,n,m; 
    int scc[maxn],scc_cnt;
    int dis[maxn],S;
    int stk[maxn],top,Maxdis,Maxpos;
    vector<int>G[maxn];
    void _init()
    {
        memset(Laxt,0,sizeof(Laxt));
        memset(dfn,0,sizeof(dfn));
        memset(scc,0,sizeof(scc));
        memset(vis,0,sizeof(vis));
        ans=cut_cnt=top=scc_cnt=cnt=times=0;    
    }
    void _add(int u,int v)
    {
          Next[++cnt]=Laxt[u];
          Laxt[u]=cnt;
          To[cnt]=v;    
    }
    void _tarjan(int u,int v){
        dfn[u]=low[u]=++times;
        int num_v=0;
        stk[top++]=u;
        for(int i=Laxt[u];i;i=Next[i]){
            if(To[i]==v){
                num_v++;
                if(num_v==1) continue;//保证重边
            }
            if(!dfn[To[i]]){
                _tarjan(To[i],u);
                if(low[u]>low[To[i]])  low[u]=low[To[i]];
                if(dfn[u]<low[To[i]])  cut_cnt++;//割边,对应缩点后是边 
            }
            else if(dfn[To[i]]<low[u]) low[u]=dfn[To[i]];
        }
        if(dfn[u]<=low[u]){//割点或者环里面第一个访问到的点(点连通缩点)
             G[++scc_cnt].clear();
             for(;;){
                 int tmp=stk[--top];
                 scc[tmp]=scc_cnt;
                 if(tmp==u) break;
             }    
        }
    }
    void _rebuild()//重建无环图 
    {
         for(int i=1;i<=n;i++)
         for(int j=Laxt[i];j;j=Next[j])
           if(scc[i]!=scc[To[j]])
           G[scc[i]].push_back(scc[To[j]]);
    }
    void _dfs(int u)
    {    
        int i,L=G[u].size();
        for(int i=0;i<L;i++)
         if(!dis[G[u][i]]){
           dis[G[u][i]]=dis[u]+1;
           _dfs(G[u][i]);
         }
    }
    void _findR()
    {
            memset(dis,0,sizeof(dis));
            dis[1]=1;S=1;Maxdis=1;
            _dfs(1);
            for(int i=1;i<=scc_cnt;i++)
                if(dis[i]>dis[S]) S=i;
            memset(dis,0,sizeof(dis));
            dis[S]=1;
            _dfs(S);
            for(int i=1;i<=scc_cnt;i++)
                if(dis[i]>Maxdis) Maxdis=dis[i];
            Maxdis--;
    }
    int main()
    {                    
        int i,j,k,u,v;
        while(~scanf("%d%d",&n,&m)){
            if(n==0&&m==0) return 0; 
            _init();
            for(i=1;i<=m;i++){
                scanf("%d%d",&u,&v);
                _add(u,v);
                _add(v,u);
            }
            for(i=1;i<=n;i++)
              if(!dfn[i])  _tarjan(i,-1);
            _rebuild();
            _findR();
            printf("%d
    ",cut_cnt-Maxdis);
        }
        return 0;
    }
  • 相关阅读:
    DHTML【11】--DOM
    sql 查询强制使用HASH连接性能测试比较
    Winform开发框架之读卡器和条码扫描枪的数据接收处理
    DevExpress的XtraReport和微软RDLC报表的使用和对比
    sql server日期时间转字符串
    C#在线更新程序[下载程序、解压缩程序、控制台程序]
    C# 定时器事件(设置时间间隔,间歇性执行某一函数,控制台程序)
    用C#用C#实现窗体在规定时间弹出,例如:10:00.弹出后关闭。并在5分钟后再次弹出。5次后停止。最好有具体代码实现窗体在规定时间弹出,例如:10:00.弹出后关闭。并在5分钟后再次弹出。5次后停止。最好有具体代码
    C#多线程学习之(五)使用定时器进行多线程的自动管理
    C# 文件与目录的基本操作(System.IO)
  • 原文地址:https://www.cnblogs.com/hua-dong/p/7694069.html
Copyright © 2011-2022 走看看