• 求解双联通分量


    参考算法指南白书P316

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <iostream>
      4 #include <algorithm>
      5 #include <vector>
      6 #include <queue>
      7 #include <set>
      8 #include <map>
      9 #include <cstring>
     10 #include <cassert>
     11 #include <cstdlib>
     12 #include <time.h>
     13 #include <stack>
     14 using namespace std;
     15 
     16 const int maxn=1000+10;
     17 
     18 int n,m;
     19 int bcc_cnt;//bcc_cnt计数一共有多少个点-双连通分量
     20 int dfs_clock;//时间戳
     21 int pre[maxn];//pre标记,同时也标记了是树中的第几个点
     22 bool iscut[maxn];//是否割点
     23 int bccno[maxn];//bccno[i]=x表示第i个顶点属于x号点双连通分量
     24 vector<int> G[maxn],bcc[maxn];  //bcc[i]中包含了第i个双连通分量的所有节点
     25 
     26 struct Edge { //边的结构体
     27     int u,v;
     28     Edge(int u,int v):u(u),v(v) {}
     29 };
     30 
     31 stack<Edge> S;
     32 
     33 int dfs(int u,int fa) {
     34     int lowu=pre[u]=++dfs_clock;
     35     int child=0;
     36     for(int i=0; i<G[u].size(); i++) {
     37         int v=G[u][i];      //取出点
     38         Edge e = Edge(u,v); //创建这条边
     39         if(!pre[v]) { //v没有被访问过
     40             S.push(e);      //将边入栈
     41             child++;
     42             int lowv=dfs(v,u);  //求low先
     43             lowu=min(lowu,lowv);
     44             if(lowv >= pre[u]) { //本节点是割点
     45                 iscut[u]=true;
     46                 bcc_cnt++;              //注意bcc_cnt从1开始编号
     47                 bcc[bcc_cnt].clear();   //清除之前留下的
     48                 while(true) {           //产生一个双连通分量,
     49                     Edge x=S.top();     //逐次取出边
     50                     S.pop();
     51                     //1个点可能属于多个连通分量,且它一定是割点。
     52                     if(bccno[x.u]!=bcc_cnt) {   //这个点还没有统计到这个连通分量。
     53                         bcc[bcc_cnt].push_back(x.u);
     54                         bccno[x.u]=bcc_cnt;     //预防重复统计
     55                     }
     56                     if(bccno[x.v]!=bcc_cnt) {
     57                         bcc[bcc_cnt].push_back(x.v);
     58                         bccno[x.v]=bcc_cnt;
     59                     }
     60                     if(x.u==u && x.v==v)      //扫到u-v,栈中又没有与u相连的边了。继续试试其他孩子
     61                         break;
     62                 }
     63             }
     64         } else if(pre[v]<pre[u]&&v!=fa) { //点在u上面就被访问过啦
     65             S.push(e);      //这个是和u在一起的双连通分量
     66             lowu=min(lowu,pre[v]);
     67         }
     68     }
     69     /*
     70     根的孩子必须大于1才会是割点,有割点才会有双连通分量。
     71     (1)那么如果根不是割点呢?
     72     假设根不是割点,那么根最多只有1个孩子,也就是说根的度为1,那么根不可能处于任何1个双连通分量中。
     73     假设根是割点,那么每个孩子各自是一个连通分量。那么就会在上面的代码中被处理为一个双联通分量。
     74     (2)如果有桥呢?比如u-v是桥,那么会怎样?
     75     假设u-v是桥,且u在数中的时间戳比较小。可知v也就是一个割点啦,u-v断开后,与v相连的都成为一个双连通分量了。
     76     回溯到u时,栈中(或顶)没有包含u的边,直到另一个连通分量的产生。
     77     如果u的孩子中没有连通分量了,那么与u相连的孩子肯定有边连到u的上边,他们又形成了一个环了,双连通分量又产生了,由其他割点去解决。
     78     */
     79     if(fa<0 && child==1) iscut[u]=false;
     80     return lowu;
     81 }
     82 
     83 void find_bcc(int n) {
     84     memset(pre,0,sizeof(pre));
     85     memset(iscut,0,sizeof(iscut));
     86     memset(bccno,0,sizeof(bccno));
     87     dfs_clock = bcc_cnt = 0;
     88     for(int i=0; i<n; i++)          //为了防止有多个连通图,全部都得搜
     89         if(!pre[i]) dfs(i,-1);
     90 }
     91 int main() {
     92     while(scanf("%d%d",&n,&m)==2&&n) {
     93         for(int i=0; i<n; i++) G[i].clear(); //点集
     94         for(int i=0; i<m; i++) {    //输入边
     95             int u,v;
     96             scanf("%d%d",&u,&v);
     97             G[u].push_back(v);
     98             G[v].push_back(u);
     99         }
    100         find_bcc(n);    //计算双连通分量的个数
    101         printf("点-双连通分量一共%d个
    ",bcc_cnt);
    102         for(int i=1; i<=bcc_cnt; i++) { //输出每个双连通分量。可能点A在第一个双连通分量中输出,又出现在第2个双连通分量中,因为它是割点。
    103             printf("第%d个点-双连通分量包含以下点:
    ",i);
    104             sort(&bcc[i][0],&bcc[i][0]+bcc[i].size()); //对vector排序,使输出的点从小到大
    105             for(int j=0; j<bcc[i].size(); j++) {
    106                 printf("%d ",bcc[i][j]);
    107             }
    108             printf("
    ");
    109         }
    110     }
    111     return 0;
    112 }
    View Code
  • 相关阅读:
    滑动窗口(单调队列)
    离散化
    leetcode第196场周赛
    WindowsForm实现警告消息框
    WindowsForm实现TextBox占位符Placeholder提示
    WindowsForm如何移动一个没有标题栏的窗口
    WindowsForm给窗口添加一些简单的动画效果
    WindowsForm切换窗口的几种常见实现
    Visual Studio Code搭建django项目
    PyInstaller打包Python源程序
  • 原文地址:https://www.cnblogs.com/ITUPC/p/5321302.html
Copyright © 2020-2023  润新知