zoukankan      html  css  js  c++  java
  • 【割点】【割边】tarjan

    洛谷割点模板题——传送门

    割边:在连通图中,删除了连通图的某条边后,图不再连通。这样的边被称为割边,也叫做桥。
    割点:在连通图中,删除了连通图的某个点以及与这个点相连的边后,图不再连通。这样的点被称为割点。
    DFS搜索树:用DFS对图进行遍历时,按照遍历次序的不同,我们可以得到一棵DFS搜索树。

    树边:在搜索树中的蓝色线所示,可理解为在DFS过程中访问未访问节点时所经过的边,也称为父子边
    回边:在搜索树中的橙色线所示,可理解为在DFS过程中遇到已访问节点时所经过的边,也称为返祖边、后向边
    观察DFS搜索树,我们可以发现有两类节点可以成为割点。对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;对非叶子节点u(非根节点),若其中的某棵子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与该棵子树的节点不再连通;则节点u为割点。对于根结点,显然很好处理;但是对于非叶子节点,怎么去判断有没有回边是一个值得深思的问题。我们用dfn[u]记录节点u在DFS过程中被遍历到的次序号,low[u]记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小),那么low[u]的计算过程如下。

    对于给的例子,其求出的dfn和low数组如下。
    id     1 2 3 4 5 6
    dfn   1 2 3 4 5 6
    low   1 1 1 4 4 4
    可以发现,对于情况2,当(u,v)为树边且low[v]≥dfn[u]时,节点u才为割点。而当(u,v)为树边且low[v]>dfn[u]时,表示v节点只能通过该边(u,v)与u连通,那么(u,v)即为割边。tarjan算法的时间复杂度是O(n+m)的,非常快。

    ——附带码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 100001;
    int n, m, cnt, rp;
    int next[2 * maxn], to[2 * maxn], head[maxn], low[maxn], dfn[maxn], father[maxn];//father为父节点
    vector <int> cut_point;
    vector < pair <int, int> > cut_edge;
    
    void add(int x, int y)
    {
        to[cnt] = y;
        next[cnt] = head[x];
        head[x] = cnt++;
    }
    
    void tarjan(int u)
    {
        int i, v, child = 0;//child表示当前节点孩子数量 
        bool flag = 0;
        dfn[u] = low[u] = ++rp;
        for(i = head[u]; i != -1; i = next[i])
        {
            v = to[i];
            if(!dfn[v])
            {
                child++;
                father[v] = u;
                tarjan(v);
                if(low[v] >= dfn[u]) flag = 1;//割点 
                if(low[v] > dfn[u]) cut_edge.push_back(make_pair(min(u, v), max(u, v)));//割边 
                low[u] = min(low[u], low[v]);
            }
            else if(v != father[u]) low[u] = min(low[u], dfn[v]);
            
        }
        //根节点若有两棵或两棵以上的子树则该为割点
        //非根节点若所有子树节点均没有指向u的祖先节点的回边则为割点
        if((father[u] == 0 && child > 1) || (father[u] && flag)) cut_point.push_back(u);
    }
    
    int main()
    {
        int i, j, x, y, s;
        memset(head, -1, sizeof(head));
        scanf("%d %d", &n, &m);
        for(i = 1; i <= m; i++)
        {
            scanf("%d %d", &x, &y);
            add(x, y);
            add(y, x);
        }
        for(i = 1; i <= n; i++)//图可能不联通(mdzz的洛谷模板题) 
         if(!dfn[i])
          tarjan(i);
        sort(cut_point.begin(), cut_point.end());
        s = cut_point.size();
        printf("%d
    ", s);
        for(i = 0; i < s; i++) printf("%d ", cut_point[i]);//输出割点 
        s = cut_edge.size();
        printf("
    %d
    ", s);
        for(i = 0; i < s; i++) printf("%d %d
    ", cut_edge[i].first, cut_edge[i].second);//输出割边 
        return 0;
    }
    View Code

    经过培训,发现上面的代码如果有重边就会拉闸。

    下面是可以应对重边的代码

     1 # include <iostream>
     2 # include <cstdio>
     3 # include <cstring>
     4 # include <string>
     5 # include <cmath>
     6 # include <vector>
     7 # include <map>
     8 # include <queue>
     9 # include <cstdlib>
    10 # define MAXN 2333
    11 using namespace std;
    12 
    13 inline void File() {
    14 #ifdef DEBUG
    15     freopen("in.txt", "r", stdin);
    16 #else
    17     //freopen();
    18     //freopen();
    19 #endif
    20 }
    21 
    22 inline int get_num() {
    23     int k = 0, f = 1;
    24     char c = getchar();
    25     for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    26     for(; isdigit(c); c = getchar()) k = k * 10 + c - '0';
    27     return k * f;
    28 }
    29 
    30 int n, m, tim, cnt;
    31 int dfn[MAXN], low[MAXN], f[MAXN], to[MAXN], next[MAXN], head[MAXN];
    32 vector <int> cut_point;
    33 vector < pair <int, int> > cut_edge;
    34 
    35 inline void add(int x, int y)
    36 {
    37     to[cnt] = y;
    38     next[cnt] = head[x];
    39     head[x] = cnt++;
    40 }
    41 
    42 inline void dfs(int u, int fa)
    43 {
    44     int i, v, child = 0;
    45     bool flag = 0;
    46     dfn[u] = low[u] = ++tim;
    47     for(i = head[u]; i != -1; i = next[i])
    48     {
    49         if((i ^ 1) == fa) continue;
    50         v = to[i];
    51         if(!dfn[v])
    52         {
    53             child++;
    54             f[v] = u;
    55             dfs(v, i);
    56             if(low[v] >= dfn[u]) flag = 1;
    57             if(low[v] > dfn[u]) cut_edge.push_back(make_pair(min(u, v), max(u, v)));
    58             low[u] = min(low[v], low[u]);
    59         }
    60         else low[u] = min(low[u], dfn[v]);
    61     }
    62     if((!f[u] && child > 1) || (f[u] && flag)) cut_point.push_back(u);
    63 }
    64 
    65 int main()
    66 {
    67     int i, j, x, y;
    68     n = get_num();
    69     m = get_num();
    70     memset(head, -1, sizeof(head));
    71     for(i = 1; i <= m; i++)
    72     {
    73         x = get_num();
    74         y = get_num();
    75         add(x, y);
    76         add(y, x);
    77     }
    78     for(i = 1; i <= n; i++)
    79         if(!dfn[i])
    80             dfs(i, -1);
    81     for(i = 0; i < cut_point.size(); i++) printf("%d
    ", cut_point[i]);
    82     puts("");
    83     for(i = 0; i < cut_edge.size(); i++) printf("%d %d
    ", cut_edge[i].first, cut_edge[i].second);
    84     puts("");
    85     return 0;
    86 }
    View Code
  • 相关阅读:
    poj 2029 Get Many Persimmon Trees 夜
    poj 1191 棋盘分割 夜
    DOM (四)
    div拖拽, onmousedown ,onmousemove, onmouseup
    闭包理解
    DOM(一)
    内存溢出与内存泄漏
    div随鼠标移动而移动(滚动条)
    对象继承模式
    DOM(二)
  • 原文地址:https://www.cnblogs.com/zhenghaotian/p/6669385.html
Copyright © 2011-2022 走看看