• 强连通(边连通和点连通) (tarjan)


    边联通:去掉这个边后,还是可以连通的

    点联通: 去掉这个点后,还是可以连通的

    目的: 就是找环,并且能把他们标记。

    dfs+queue的思想(L版本)

    tarjain 算法(可能打错了,欸嘿)

    注意:

    • 经常和 缩点联系在一起
    • 入度出度的思想
    • 拓扑排序等等
    • 这zhuliu算法有相关的

    边连通: 

    void tj(int a)
    {
        low[a]=dfn[a]=++cur;
        qu[++r]=a;vis[a]=1;
        for(ri i=0;i<p[a].size();i++)
        {
            int b=p[a][i];
            if(!dfn[b])
            {
                dfs(b);
                low[a]=min(low[a],low[b]);
            }
            else if(vis[b]) low[a]=min(dfn[b],low[a]); // 返祖边,有向边,要在vis还存在的时候,不然他不存在,说明人家不会到你这边来 
        }
        if(low[a]==dfn[a])
        {
            cnt++;
            while(r>=1)
            {
                int tmp=qu[r--];
                id[tmp]=cnt;vis[tmp]=0; // important, 把vis去掉 
                if(tmp==a) break;
            }
        }
    }
    View Code(有向边)
    void tj(int a,int f)
    {
        low[a]=dfn[a]=++cur;
        qu[++r]=a;
        for(ri i=0;i<p[a].size();i++)
        {
            int b=p[a][i];
            if(!dfn[b])
            {
                dfs(b,a);
                low[a]=min(low[a],low[b]);
            }
            else if(b!=f) low[a]=min(dfn[b],low[a]); // 返祖边,无向边,一定不要是父亲,(这个时候一般都是告诉了你没有重边) 
        }
        if(low[a]==dfn[a])
        {
            cnt++;
            while(r>=1)
            {
                int tmp=qu[r--];
                id[tmp]=cnt; 
                if(tmp==a) break;
            }
        }
    }
    View Code(无向边)
    #include <bits/stdc++.h>
    using namespace std;
    #define ri register int 
    #define M 2000005
    
    int n,m;
    vector <int>p[M];
    int dfn[M],low[M],cur;
    int qu[M],r;
    
    vector<int>pp[M];
    int cnt=0;
    void tj(int a,int f)
    {
        low[a]=dfn[a]=++cur;
        qu[++r]=a;
        int tmp=0;
        for(ri i=0;i<p[a].size();i++)
        {
            int b=p[a][i];
            if(dfn[b]==0)
            {
                tj(b,a);
                low[a]=min(low[b],low[a]);
            }
            else if(b!=f||(b==f&&tmp)) low[a]=min(low[a],dfn[b]);
            if(b==f) tmp++;
        }
        if(low[a]==dfn[a])
        {
            cnt++;
            while(r>=1)
            {
                int b=qu[r--];
                pp[cnt].push_back(b);
                if(b==a) break;
            }
        }
    }
    
    
    int main(){
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        
        cin>>n>>m;
        for(ri i=1;i<=m;i++)
        {
            int a,b;
            cin>>a>>b;
            p[a].push_back(b);
            p[b].push_back(a);
        }
        for(ri i=1;i<=n;i++)
        if(dfn[i]==0) tj(i,0);
        cout<<cnt<<"\n";
        for(ri i=1;i<=cnt;i++)
        {
            cout<<pp[i].size()<<" ";
            for(ri j=0;j<pp[i].size();j++)
            {
                cout<<pp[i][j]<<" ";
            }
            cout<<"\n";
        }
        return 0;
        
    }
    无向边(有重边情况)

    求割点:

    • 割点: 删除这个点后, 连通块的数量会增加
    • low[b]>dfn[a], 不是根节点,或者是根节点,但是有2个环或者链, 
    #include <bits/stdc++.h>
    using namespace std;
    #define ri register int 
    #define M 2000005
    
    int n,m;
    vector <int> p[M];
    int low[M],dfn[M],cur;
    int ge[M];
    int root;
    int ans=0;
    void tj(int a,int f){
        
        low[a]=dfn[a]=++cur;
        int cc=0,cc2=0;
        for(ri i=0;i<p[a].size();i++)
        {
            int b=p[a][i];
            if(dfn[b]==0)
            {
                tj(b,a);
                low[a]=min(low[b],low[a]);
                if(ge[a]) continue;
                if(low[b]>=dfn[a])
                {   
                    cc++;
                    if(a!=root||cc>=2) ge[a]=1,ans++;
                }
            }else if(a!=f||cc2)) low[a]=min(dfn[b],low[a]);
            if(b==f) cc2++;
        }    
    }
    int main(){
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        
        cin>>n>>m;
        for(ri i=1;i<=m;i++)
        {
            int a,b;
            cin>>a>>b;
            p[a].push_back(b);
            p[b].push_back(a);
        }
        
         for(ri i=1;i<=n;i++)
         {
             if(dfn[i]==0)
             {
                 root=i;
                 tj(i,0);
             }    
        }
        
        cout<<ans<<"\n";
        for(ri i=1;i<=n;i++)
        {
            if(ge[i]) cout<<i<<" ";
        }
        
        return 0;
        
    }
    View Code

    注意有向边和无向边的区别 

    • 无向: 儿子不能是父亲
    • 有向: 3个vis

    应用:

    • 无向图 缩完点之后就是一个 树!!

     后记:

    • 遇到图论题 一定要看他是不是连通的
    • for(----) if() tj(i); 

    例题:

    题目描述
    JYY 最近痴迷于图的强连通性,所以对于任何有向图,JYY 都希望增加一些边使得这个图变成强连通图。JYY现在得到了一个 nn 个点 mm 条边的有向图,所有点从 11 到 nn 编号。
    
    JYY 想知道:
    
    在给定的图中,最多能选出多少个点,使得这些点在原图中两两可达?
    
    在给定的图中,最少增加多少条边,可以使得这个图变成强连通图?
    
    其中,一个有向图 G(V,E)G(V,E)是强连通的,当且仅当任意顶点 a,b\in V,a\neq ba,b∈V,a
    
    =b之间都存在 a\to ba→b 和 b\to ab→a 的路径。
    
    输入格式
    第一行包含两个整数 nn 和 mm。
    
    接下来 mm 行,每行两个整数 xx 和 yy,表示图中有一条从 xx 到 yy 的有向边。
    
    输出格式
    两行,第一行表示第一个问题的答案,第二行表示第二个问题的答案。
    
    输入输出样例
    输入 #1复制
    4 3
    1 4
    2 3
    2 4
    输出 #1复制
    1
    2
    说明/提示
    样例解释 1
    对于第一个问题,无法选出互相连通两个点,答案为 11。
    
    对于第二个问题,一种加边数最小的方案为 (3,1)(3,1) 和 (4,2)(4,2),答案为 22。
    
    数据范围
    对于 100\%100% 的数据,1\leq n\leq 10^5,1\leq m\leq 3\times 10^51≤n≤10 
    5
     ,1≤m≤3×10 
    5
    View Problem
    #include <bits/stdc++.h>
    using namespace std;
    #define ri register int
    #define M 100005
    
    template <class G > void read(G &x)
    {
        x=0;int f=0;char ch=getchar();
        while(ch<'0'||ch>'9'){f|=ch=='-';ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x=f?-x:x;
        return ;
    }
    
    int n,m;
    
    vector <int> p[M];
    int low[M],dfn[M],vis[M];
    int flag[M],tot;
    int cent;
    int q[M];int ans;
    int l;
    void tj(int a)
    {
        dfn[a]=low[a]=++cent;
        q[++l]=a;
        vis[a]=1;
        for(ri i=0;i<p[a].size();i++)
        {
            int b=p[a][i];
            if(!dfn[b])
            {
                tj(b);
                low[a]=min(low[a],low[b]);
            }
            else if(vis[b]) low[a]=min(low[a],dfn[b]);
        }
        if(low[a]==dfn[a])
        {
            tot++;int tmp=0;
            while(l>=1)
            {
                int b=q[l];l--;
                vis[b]=0;
                flag[b]=tot;tmp++;
                if(b==a) break;
            }
            ans=max(ans,tmp);
        }
    }
    int in[M],out[M];
    int main(){
        
        
        read(n);read(m);
        for(ri i=1;i<=m;i++)
        {
            int a,b;read(a);read(b);
            p[a].push_back(b);
        }
        
        for(ri i=1;i<=n;i++) // attention
        {
            if(!dfn[i]) tj(i);
        }
        
        for(ri i=1;i<=n;i++)
        {
            for(ri j=0;j<p[i].size();j++)
            {
                int b=p[i][j];
                if(flag[i]==flag[b]) continue;
                out[flag[i]]++;in[flag[b]]++;
            }
        }
       int IN=0,OUT=0;
       for(ri i=1;i<=tot;i++)
       {
             if(out[i]==0) OUT++;
             if(in[i]==0) IN++;
       }
       printf("%d\n",ans);
       ans=max(OUT,IN);
       printf("%d",ans);
       return 0;
        
        
        
        
        
        
        
        
    } 
    View Code

     还有一个: swjtu—春季集训第三场 - Virtual Judge (vjudge.net)

  • 相关阅读:
    Salesforce和SAP Netweaver里数据库表的元数据设计
    Salesforce平台支持多租户Multi tenant的核心设计思路
    S/4HANA生产订单增强WORKORDER_UPDATE方法BEFORE_UPDATE参数分析
    S/4HANA生产订单的标准状态和透明工厂原型状态的映射
    1048. Find Coins (25)
    1101. Quick Sort (25)
    1009. Product of Polynomials (25)
    pta 奇数值结点链表&&单链表结点删除
    1007. Maximum Subsequence Sum (25)
    1006. Sign In and Sign Out (25)
  • 原文地址:https://www.cnblogs.com/Lamboofhome/p/15990184.html
Copyright © 2020-2023  润新知