zoukankan      html  css  js  c++  java
  • 【HDU 4612 Warm up】BCC 树的直径

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4612

    题意:一个包含n个节点m条边的无向连通图(无自环,可能有重边)。求添加一条边后最少剩余的桥的数目。

    思路:要想尽可能地消灭桥,那么添加的这条边一定是连通了最多的BCC。

      所以首先进行双连通分量分解,并记录桥的数目;然后将同属一个BCC的点缩成一个,代之以block序号,以block序号为点将原图重构为一棵树。

      最后求树的直径,桥的数目减去树的直径即为答案。

    整体框架是学习了 http://www.cnblogs.com/kuangbin/archive/2013/07/25/3214879.html 的代码。一些细节是自己想的。

    学习到的缩点的姿势:在BCC分解后,为每条边是否为桥打上了标记,同时belong数组已记录了每个点所属的连通块号(关节点每次不退栈,而归属于它所找到的最后一个BCC);这时遍历一遍所有点,如果点 u 所邻接的某条边 i 是桥,那么这条边一定是重构出树中的边,即这条边的终点为 v ,假如用vector<int>形的邻接表 G 存新图,此时应在u和v各自所属的连通块之间加一条边,即G[belong[u]].push_back(belong[v])。

    学习到的处理重边的姿势:先把所有边按字典序排序,此时重边必然紧邻,所以addEdge时判一下相邻的边是否相同即可决定这条边是否打重边标记。

    这里我用两重while循环、一个flagDup标记以及两个指针cur, i来处理,有点像尺取法:i 与 cur相同的话一直向前走,当遇到第一个 i != cur 时,通过判flagDup来决定当前从开始cur 到 i 之前的边是否打重边标记。开始在边界判断上出了点问题,注意内层循环要保证 i < m。

    由于是无向图,同一条边在两个端点各登记一次,所以在边数组里有两个副本,但可以保证两条边的序号仅相差1,这样也可以通过与 1 异或方便地找到另一个副本。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 #include <vector>
      5 #define CLEAR(A, X) memset(A, X, sizeof(A))
      6 #define REP(N) for(int i=0; i<(N); i++)
      7 #define REPE(N) for(int i=1; i<=(N); i++)
      8 #define FREAD(FN) freopen((FN), "r", stdin)
      9 #define pb(a) push_back(a)
     10 
     11 using namespace std;
     12 
     13 const int MAX_N = 200005;
     14 const int MAX_M = 2000005;
     15 
     16 struct Edge
     17 {
     18     int v, next;
     19     bool isBrg;
     20     bool more;//重边
     21 }edges[MAX_M];
     22 
     23 int head[MAX_N], numE;
     24 int low[MAX_N], dfn[MAX_N], S[MAX_N], belong[MAX_N];
     25 int clock, topS;
     26 int block;
     27 bool inStack[MAX_N];
     28 int numBrg;
     29 
     30 void addEdge(int u, int v, bool isDup){
     31     edges[numE].v = v;
     32     edges[numE].next = head[u];
     33     edges[numE].isBrg = 0;
     34     edges[numE].more = isDup;//是否重边
     35     head[u] = numE++;
     36 
     37     //反向边,序号仅差1
     38     edges[numE].v = u;
     39     edges[numE].next = head[v];
     40     edges[numE].isBrg = 0;
     41     edges[numE].more = isDup;
     42     head[v] =numE++;
     43 }
     44 
     45 void bcc(int u, int p, bool isDup){
     46     low[u] = dfn[u] = ++clock;
     47     S[topS++] = u;
     48     inStack[u] = 1;
     49     for(int i=head[u]; i != -1; i = edges[i].next){
     50         int v = edges[i].v;
     51         if(v == p && (!isDup)) continue;//parent and no duplicate
     52         if(!dfn[v]){//tree
     53             bcc(v, u, edges[i].more);
     54             low[u] = min(low[u], low[v]);
     55             if(low[v] > dfn[u]){//bridge
     56                 numBrg++;
     57                 edges[i].isBrg = 1;
     58                 edges[i^1].isBrg = 1;
     59             }
     60         }else if(inStack[v])//backward or parent
     61             low[u] = min(low[u], dfn[v]);//include parent
     62     }
     63     if(low[u] == dfn[u]){//new bcc
     64         block++;
     65         int t;
     66         do{
     67             t = S[--topS];
     68             inStack[t] = 0;
     69             belong[t] = block;
     70         }while(t != u);
     71     }
     72 }
     73 
     74 void init(){
     75     numE = 0;
     76     numBrg = 0;
     77     CLEAR(head, -1);
     78     CLEAR(low, 0); CLEAR(dfn, 0); 
     79     CLEAR(inStack, 0);
     80     CLEAR(belong, 0);
     81     clock = block = topS = 0;
     82 }
     83 
     84 vector<int> G[MAX_N];//缩点后的新图
     85 int dep[MAX_N];
     86 
     87 void dfs(int u){
     88     for(int i=0; i<G[u].size(); i++){
     89         int v = G[u][i];
     90         if(dep[v] == -1){
     91             dep[v] = dep[u]+1;
     92             dfs(v);
     93         }
     94     }
     95 }
     96 
     97 int n, m;
     98 struct Node
     99 {
    100     int u, v;
    101 }nodes[MAX_M];
    102 bool cmp(Node a, Node b){
    103     if(a.u == b.u) return a.v < b.v;
    104     return a.u < b.u;
    105 }
    106 bool same(Node a, Node b){
    107     if(a.u == b.u && a.v == b.v) return true;
    108     return false;
    109 }
    110 
    111 int main()
    112 {
    113     FREAD("4612.txt");
    114     while(~scanf("%d%d", &n, &m) && n && m){
    115         init();
    116         for(int i=0; i<m; i++){
    117             int u, v;
    118             scanf("%d%d", &u, &v);
    119             if(u > v) swap(u, v);
    120             nodes[i].u = u; nodes[i].v = v;
    121         }
    122         sort(nodes, nodes+m, cmp);//将重边聚到一起
    123         int cur = 0, i = 1;
    124         int flagDup = 0;
    125         while(cur < m){
    126             while(i<m && same(nodes[cur], nodes[i])){
    127                 flagDup = 1;//与cur重
    128                 i++;
    129             }
    130             if(flagDup) addEdge(nodes[cur].u, nodes[cur].v, 1);
    131             else addEdge(nodes[cur].u, nodes[cur].v, 0);
    132             //printf("add edge %d %d
    ", nodes[cur].u, nodes[cur].v);
    133             flagDup = 0;
    134             cur = i++;
    135         }
    136         
    137         bcc(1, 0, 0);//0->1虚拟边
    138         REPE(block) G[i].clear();//block个节点的图
    139         REPE(n){//以block为编号,[1,block]建新图
    140             for(int j=head[i]; j!=-1; j=edges[j].next){
    141                 if(edges[j].isBrg){
    142                     int u = i;
    143                     int v = edges[j].v;
    144                     G[belong[u]].pb(belong[v]);//缩点
    145                 }
    146             }
    147         }
    148         CLEAR(dep, -1);
    149         dep[1] = 0;//以1为根
    150         dfs(1);
    151         int deepest = 1, maxDep = -1;
    152         REPE(block){//找到最深的
    153             if(dep[i] > dep[deepest]){
    154                 maxDep = dep[deepest];
    155                 deepest = i;
    156             }
    157         }
    158         CLEAR(dep, -1);
    159         dep[deepest] = 0;//以deepest为根
    160         dfs(deepest);
    161         int len = 0;//树的直径
    162         REPE(block) len = max(len, dep[i]);
    163 
    164         printf("%d
    ", numBrg - len);
    165     }
    166     return 0;
    167 }

    这道题从MLE改到WA改到RE再改到TLE,最终弃掉自己的版本学习了bin神的。发现他的实现很真实地反映了思路,比如bcc时,增加父节点p和边属性isDup两个参数,这样就通过v!=p && (!isDup)把无重边的父子边过滤掉,剩下真的后向边和有重边的父子边。我在从思路到代码的转换上还需要多加练习。

  • 相关阅读:
    redis常用数据类型与命令
    bcb6重启应用程序
    MySQL 关联查询  外连接 { LEFT| RIGHT } JOIN
    MySQL 关联查询 内连接
    MySql子查询
    MySql单表查询
    表级操作语句
    库级操作语句
    14.正则表达式、re模块、元字符
    13.生成器、迭代器、 模块、包和包管理
  • 原文地址:https://www.cnblogs.com/helenawang/p/5545211.html
Copyright © 2011-2022 走看看