zoukankan      html  css  js  c++  java
  • POJ 3352 Road Construction(边双连通分量,桥,tarjan)

    题解转自http://blog.csdn.net/lyy289065406/article/details/6762370   文中部分思路或定义模糊,重写的红色部分为修改过的。

    大致题意:

      某个企业想把一个热带天堂岛变成旅游胜地,岛上有N个旅游景点,保证任意2个旅游景点之间有路径连通的(可间接连通)。而为了给游客提供更方便的服务,该企业要求道路部门在某些道路增加一些设施。

      道路部门每次只会选择一条道路施工,在该条道路施工完毕前,其他道路依然可以通行。然而有道路部门正在施工的道路,在施工完毕前是禁止游客通行的。这就导致了在施工期间游客可能无法到达一些景点。

      为了在施工期间所有旅游景点依然能够正常对游客开放,该企业决定搭建一些临时桥梁,使得不管道路部门选在哪条路进行施工,游客都能够到达所有旅游景点。给出当下允许通行的R条道路,问该企业至少再搭建几条临时桥梁,才能使得游客无视道路部门的存在到达所有旅游景点?

      题目所给的图是连通的,且所给的边没有重复的,所以不考虑重边。

       注意:不要把Sample Input 1读入,更不要把它输出!!!!!

    首先建立模型:

           给定一个连通的无向图G,至少要添加几条边,才能使其变为边-双连通图(因为在修道路是一条边!)

           模型很简单,正在施工的道路我们可以认为那条边被删除了。那么一个图G能够在删除任意一条边后,仍然是连通的,当且仅当图G至少为边双连通的。

           PS:不要问我为什么不是3-连通、4-连通...人家题目问“至少添加几条边”好不...

           显然,当图G存在桥(割边)的时候,它必定不是边双连通的。桥的两个端点必定分别属于图G的两个【边双连通分量】(注意不是点双连通分量),一旦删除了桥,这两个【边双连通分量】必定断开,图G就不连通了。但是如果在两个【边双连通分量】之间再添加一条边,桥就不再是桥了,这两个【边双连通分量】之间也就是边双连通了。

           那么如果图G有多个【边双连通分量】呢?至少应该添加多少条边,才能使得任意两个【边双连通分量】之间都是边双连通的(也就是图G是边双连通的)?

           这个问题就是本题的问题。要解决这个问题:

    1、  首先要找出图G的所有【边双连通分量】。

    Tarjan算法用来寻找图G的所有【边双连通分量】是最简单有效的方法,因为Tarjan算法在DFS过程中会对图G所有的结点都生成一个Low值,而由于题目已表明任意两个结点之间不会出现重边,因此Low值相同的两个结点必定在同一个【边双连通分量】中(但是!!low值不同的点并不代表就不在同一个边双连通分量中,因为由于先后访问的顺序的原因,low值可能会不同,这跟并查集的道理是一样的,如果不进行压缩路径,几乎很多人的直接上级都不会是该团队的boss,这点可以模仿并查集的方式将其归到只有两层,查起来就很快了,否则可能超时!!当然你不考虑这点也能AC,水果了那些简单数据,但是下面这2组数据你可能过不了)!  

    11 14
    1 2
    1 3
    1 4
    2 5
    6 11
    2 6
    5 6
    5 11
    3 7
    3 8
    7 8
    4 9
    4 10
    9 10

    11 14
    1 2
    1 3
    1 4
    5 11
    2 5
    2 6
    5 6
    6 11
    3 7
    3 8
    7 8
    4 9
    4 10
    9 10

    以上两个测试数据的ans都是2。

    2、  把每一个【边双连通分量】都看做一个点(即【缩点】)

    也有人称【缩点】为【块】,都是一样的。其实缩点不是真的缩点,只要利用Low值对图G的点分类处理,就已经缩点了。

    以样例1为例,样例1得到的图G为上左图,

    其中Low[4]=Low[9]=Low[10]

           Low[3]=Low[7]=Low[8]

           Low[2]=Low[5]=Low[6]

           Low[1]独自为政....

    把Low值相同的点划分为一类,每一类就是一个【边双连通分量】,也就是【缩点】了,不难发现,连接【缩点】之间的边,都是图G的桥,那么我们就得到了上右图以缩点为结点,以桥为树边所构造成的树。

      以上这样以low值判断边连通分量是没有错的!只是要确保low值能区分出该块,当然,你可以用栈,就像求点双连通分量那样(并不是完全一样,那是以割点来区分的,这是以桥来区分的)。

    3、  问题再次被转化为“至少在缩点树上增加多少条树边,使得这棵树变为一个边双连通图”。

    首先知道一条等式:

    若要使得任意一棵树,在增加若干条边后,变成一个双连通图,那么

    至少增加的边数 =( 这棵树总度数为1的结点数 + 1 )/ 2

    (证明就不证明了,自己画几棵树比划一下就知道了)

    那么我们只需求缩点树中总度数为1的结点数(不一定是叶子数,如果树根仅有1个孩子,同样要将其统计进来)有多少就可以了。换而言之,我们只需求出所有缩点的度数,然后判断度数为1的缩点有几个,问题就解决了。

    4、  求出所有缩点的度数的方法

    两两枚举图G的直接连通的点(如果求出了桥,那么枚举桥也行的,相同于直接枚举树的边),只要这两个点不在同一个【缩点】中,那么它们各自所在的【缩点】的度数都+1。注意由于图G时无向图,这样做会使得所有【缩点】的度数都是真实度数的2倍,必须除2后再判断是否度为1。

     特别注意:求【点双连通分量】与【边双连通分量】是不同的模板,勿混淆。

     1 #include <iostream>
     2 #include <cmath>
     3 #include <cstdio>
     4 #include <vector>
     5 #include <cstring>
     6 #include <set>
     7 //#include <bits/stdc++.h>
     8 using namespace std;
     9 const int N=1000+5;
    10 vector<int> vect[N];
    11 int low[N], dfn[N],  cnter;
    12 int du[N];
    13 int pre[N];//这个与dfn是相反的索引
    14 vector<pair<int,int> >  cutt;
    15 int find(int x) //寻找x的low值
    16 {
    17     if(low[x]==dfn[x])  return low[x];
    18     return low[x]=find( pre[low[x] ] );
    19 }
    20 
    21 void DFS(int x, int far)
    22 {
    23     low[x]= dfn[x]= ++cnter;
    24     pre[cnter]=x;   //标记第cnter个访问的是谁
    25     for(int i=0; i<vect[x].size(); i++)
    26     {
    27         int t=vect[x][i];
    28         if(!dfn[t])
    29         {
    30             DFS(t,x);
    31             low[x]=min(low[x],low[t]);
    32             if(low[t]>dfn[x])   cutt.push_back(make_pair(x,t));//桥(即树边)
    33         }
    34         else if(t!=far)    low[x]=min(low[x],dfn[t]);
    35     }
    36 }
    37 
    38 int cal_bcc(int f)
    39 {
    40     cutt.clear();
    41     memset(du,0,sizeof(du));
    42     memset(low,0,sizeof(low));
    43     memset(dfn,0,sizeof(dfn));
    44     memset(pre,0,sizeof(pre));
    45     cnter=0;
    46     DFS(1,0);
    47     for(int i=0; i<cutt.size(); i++)
    48     {
    49         int a=cutt[i].first;
    50         int b=cutt[i].second;
    51         du[find(a)]++;
    52         du[find(b)]++;
    53     }
    54 
    55     int ans=0;
    56     for(int i=1; i<=f; i++)    if(du[i]==1)    ans++;
    57     return ((ans+1)/2);
    58 }
    59 
    60 int main()
    61 {
    62     //freopen("input.txt", "r", stdin);
    63     int f, r, a, b, j=0;
    64     char s[N];
    65     while(cin>>f>>r)
    66     {
    67         for(int i=1; i<=f; i++) vect[i].clear();
    68         while(r--)
    69         {
    70             scanf("%d%d", &a, &b);
    71             vect[a].push_back(b);
    72             vect[b].push_back(a);
    73         }
    74         printf("%d
    ",cal_bcc(f));
    75     }
    76     return 0;
    77 }
    AC代码
  • 相关阅读:
    linux -- 基于zookeeper搭建yarn的HA高可用集群
    Linux -- 之HDFS实现自动切换HA(全新HDFS)
    Hadoop格式化 From hu-hadoop1/192.168.11.11 to hu-hadoop2:8485 failed on connection exception: java.net.
    Directory /home/hdfs/name is in an inconsistent state: storage directory does not exist or is not a
    react学习01
    单页面应用(spa)引入百度地图(Cannot read property 'dc' of undefined)
    npm 发布包
    Vue学习-01
    echarts3.0使用总结
    webpack学习--创建一个webpack打包流程
  • 原文地址:https://www.cnblogs.com/xcw0754/p/4619594.html
Copyright © 2011-2022 走看看