• [模板]割點/割邊


    tarjan最重要的之一應該就是dfn和low兩個數組吧,這裡判割邊和割點都是根據的這兩個數組

    這裡的思路來自李煜東算法競賽進階指南,好像還有一些其他寫法,但是自己比較菜不會......

    注意這裡的寫法cnt(邊的編號)要初始化為1


    一、割邊

    對於割邊的判定,需要:

      搜索樹上存在一個x的子節點y要滿足dfn[x]<low[y]

    意思就是如果從x的子樹出發,在不經過邊(x,y)的情況下,不能到達比x更早訪問的節點,就說明邊(x,y)是一個割邊

    對於無向圖,因為存的時候按照有向圖存的,每一條無向邊一定是成對存的,所以異或1就能變成另一條邊,

    若沿著編號為i的邊遞歸進入了節點x,則忽略從x出發編號為i xor 1的邊,通過其它邊計算low[x](書上抄的)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int maxn=100010;
    int n,m,num;
    int head[maxn],cnt=1;//cnt初始化為1? 
    struct node{
        int v,nxt;
    }e[maxn*2];
    int dfn[maxn],low[maxn];
    bool bridge[maxn*2];
    void add(int u,int v){
        e[++cnt].v=v;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }
    void tarjan(int x,int fa){
        dfn[x]=low[x]=++num;
        for(int i=head[x];i;i=e[i].nxt){
            int y=e[i].v;
            if(!dfn[y]){
                tarjan(y,i);
                low[x]=min(low[x],low[y]);
                if(low[y]>dfn[x])bridge[i]=bridge[i^1]=1;
                //異或是為了處理無向邊,每對無相邊一定是異或的關係 
            }
            else if(i!=(fa^1))
            low[x]=min(low[x],dfn[y]);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1,x,y;i<=m;i++){
            scanf("%d%d",&x,&y);
            add(x,y);add(y,x);
        }
        for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,0);
        for(int i=2;i<cnt;i+=2)
        if(bridge[i])printf("%d %d
    ",e[i^1].v,e[i].v);
    }

    二、割點

    對於割點的判定,需要:

       若x不是搜索樹的根節點,則搜索樹上x的一個子節點y要滿足dfn[x]<=low[y]

    特別的,如果x是搜索樹的根節點,則x是割點當且僅當搜索樹上存在至少兩個子節點y1,y2滿足上述條件

    證明方法遇割邊類似

    因為判定條件是小於等於號,所以不必考慮父節點和重邊的問題

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int maxn=100010;
    int n,m,num,root,sum;
    int head[maxn],cnt=1;
    int dfn[maxn],low[maxn],st[maxn];
    struct node{
        int v,nxt;
    }e[maxn*2];
    void add(int u,int v){
        e[++cnt].v=v;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }
    bool cut[maxn];
    void tarjan(int x)
    {
        dfn[x]=low[x]=++num;
        int fl=0;//兒子個數 
        for(int i=head[x];i;i=e[i].nxt){
            int y=e[i].v;
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
                if(low[y]>=dfn[x]){//割點判定條件
                    fl++;
                    if(x!=root || fl>1)cut[x]=1;//根節點必須保證至少子樹有兩個節點 
                }
            }
            else low[x]=min(low[x],dfn[y]);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1,x,y;i<=m;i++){
            scanf("%d%d",&x,&y);
            if(x==y)continue;//自環 
            add(x,y);add(y,x);
        }
        for(int i=1;i<=n;i++)
        if(!dfn[i])root=i,tarjan(i);
        for(int i=1;i<=n;i++)if(cut[i])sum++;//必須最後再數 
        printf("%d
    ",sum);
        for(int i=1;i<=n;i++)
        if(cut[i])printf("%d ",i);
    }
  • 相关阅读:
    Element-ui 复选框使用问题
    脚手架 oss 直传
    门外汉 avue使用
    cookie关闭浏览器清除
    关于(Incorrect string value: 'xF0x9Fx8ExBE' for column 'xxx' at row)报错
    扫码登录
    微信支付
    vue 腾讯地图 javaScriptAPI GL 多个markers自适应 (3)
    vue props 一次性传多个值
    vuex 的简单使用
  • 原文地址:https://www.cnblogs.com/superminivan/p/10697547.html
Copyright © 2020-2023  润新知