缩点|强连通分量

Posted cj-gjh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了缩点|强连通分量相关的知识,希望对你有一定的参考价值。

缩点|强连通分量一般解释自行百度,反正比较简单,一般性用在有向图中,可结合DP乱搞做题.

缩点

顾名思义,就是把一些点缩成几个点.

强连通分量

在一个有向图中,一个环就是一个强连通分量.

求法

我们不妨设dfn[i]表示到达第i个点的时间,low[i]表示第i个点可以到达的祖先中dfn的最小值.
这里我们引入几个概念(一个图中白点表示没有访问过的点,灰点表示访问了但是没有回溯到的点,黑点表示已经回溯了的点):

  1. 到白点的边为树边(T)
  2. 到灰点的边为返祖边(后向边)
  3. 到黑点的边为前向边

那么现在我们就可以做了

code

void Tarjan(int u){//当前访问到了u
    dfn[u]=low[u]=++time;//时间戳
    sta[++num]=u;ins[u]=1;//标记为灰点并进栈
    for(int i=front[u];i;i=e[i].nxt){//链式前向星
        int v=e[li].to;
        if(!dfn[v]){//是个白点
            Tarjan(v);
            low[u]=min(low[u],low[v]);
            //low[u]为u和v能够到达的最小的祖先的编号
        }
        else if(ins[v])low[u]=min(low[u],dfn[v]);
        //这句话十分的神奇,因为有可能v的low值是有一条前向边.
    }
    //以上便完成了对low值的更新
    if(dfn[u]==low[u]){//能够回到的最早祖先就是自己,说明是个强连通分量
        int j;t++;//t为强连通分量的个数
        do{
            j=sta[num--];belong[j]=t;ins[j]=0;
            //出栈操作并将j归为第t个强连通分量
        }while(j!=u);
    }
}

以上便是代码

例题

lg2835

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
#define ll long long
#define in inline
#define re register
in int gi(){
    int sum=0,f=1;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();} 
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return sum*f;
}
const int maxn=210,maxm=40010;
struct node{
    int to,nxt;
}e[maxm];
int front[maxn],cnt,chu[maxn],inp[maxn];
void Add(int u,int v){
    e[++cnt]=(node){v,front[u]};front[u]=cnt;
}
int dfn[maxn],low[maxn],tim;
int sta[maxn<<1],num,color[maxn],t;
void Trajan(int u){
    dfn[u]=low[u]=++tim;sta[++num]=u;
    inp[u]=1;
    for(int i=front[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(!dfn[v]){
            Trajan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(inp[v])low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        int j;t++;
        do{
            j=sta[num--];inp[j]=0;
            color[j]=t;
        }while(j!=u);
    }
}
int main(){
    int i,j,k,n,m;
    n=gi();
    for(i=1;i<=n;i++){
        m=gi();
        while(m){
            Add(i,m);
            m=gi();
        }
    }
    for(i=1;i<=n;i++)
        if(!dfn[i])Trajan(i);
    for(i=1;i<=n;i++)
        for(j=front[i];j;j=e[j].nxt){
            int v=e[j].to;
            if(color[i]!=color[v])chu[color[v]]=1;
        }
    int ans=0;
    for(i=1;i<=t;i++)
        if(!chu[i])ans++;
    printf("%d\n",ans);
    return 0;
}

lg3916

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
#define ll long long
#define in inline
#define re register
in int gi(){
    int sum=0,f=1;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();} 
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return sum*f;
}
const int maxn=100010,maxm=100010;
struct node{
    int to,nxt;
}e[maxm];
int front[maxn],cnt,ins[maxn],num,t;
void Add(int u,int v){
    e[++cnt]=(node){v,front[u]};front[u]=cnt;
}
int low[maxn],dfn[maxn],tim,ma[maxn],color[maxn],sta[maxm],A[maxn];
void dfs(int u,int d){
    if(A[u])return;
    A[u]=d;
    for(int i=front[u];i;i=e[i].nxt){
        int v=e[i].to;
        dfs(v,d);
    }
}
int main(){
    int i,j,k,n,m;
    n=gi(),m=gi();
    for(i=1;i<=m;i++){
        int u=gi(),v=gi();
        Add(v,u);
    }
    for(i=n;i>=1;i--)dfs(i,i);
    for(i=1;i<=n;i++)
        printf("%d%c",A[i],i==n?'\n':' ');
    return 0;
}
//这道题目和Trajan没有很大的关系...

lg2863

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
#define ll long long
#define in inline
#define re register
in int gi(){
    int sum=0,f=1;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();} 
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return sum*f;
}
const int maxn=10010,maxm=50010;
struct node{
    int to,nxt;
}e[maxm];
int front[maxn],cnt,dfn[maxn],low[maxn],color[maxn],t,tim,siz[maxn],sta[maxn],ins[maxn],num;
void Add(int u,int v){
    e[++cnt]=(node){v,front[u]};front[u]=cnt;
}
void Trajan(int u){
    dfn[u]=low[u]=++tim;
    sta[++num]=u;ins[u]=1;
    for(int i=front[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(!dfn[v]){Trajan(v);low[u]=min(low[u],low[v]);}
        else if(ins[v])low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        int j;t++;
        do{
            j=sta[num--];ins[j]=0;
            color[j]=t;siz[t]++;
        }while(j!=u);
    }
}
int main(){
    int i,j,k,n,m;
    n=gi();m=gi();
    for(i=1;i<=m;i++){
        int u=gi(),v=gi();
        Add(u,v);
    }
    for(i=1;i<=n;i++)
        if(!dfn[i])Trajan(i);
    int ans=0;
    for(i=1;i<=t;i++)
        if(siz[i]>1)ans++;
    printf("%d\n",ans);
    return 0;
}

lg2002

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
#define ll long long
#define in inline
#define re register
in int gi(){
    int sum=0,f=1;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();} 
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return sum*f;
}
const int maxn=100010,maxm=500010;
struct node{
    int to,nxt;
}e[maxm];
int front[maxn],cnt,chu[maxn],inp[maxn];
void Add(int u,int v){
    e[++cnt]=(node){v,front[u]};front[u]=cnt;
}
int dfn[maxn],low[maxn],tim;
int sta[maxn<<1],num,color[maxn],t;
void Trajan(int u){
    dfn[u]=low[u]=++tim;sta[++num]=u;
    inp[u]=1;
    for(int i=front[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(!dfn[v]){
            Trajan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(inp[v])low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        int j;t++;
        do{
            j=sta[num--];inp[j]=0;
            color[j]=t;
        }while(j!=u);
    }
}
int main(){
    int i,j,k,n,m;
    n=gi();m=gi();
    for(i=1;i<=m;i++){
        int u=gi(),v=gi();
        Add(u,v);
    }
    for(i=1;i<=n;i++)
        if(!dfn[i])Trajan(i);
    for(i=1;i<=n;i++)
        for(j=front[i];j;j=e[j].nxt){
            int v=e[j].to;
            if(color[i]!=color[v])chu[color[v]]=1;
        }
    int ans=0;
    for(i=1;i<=t;i++)
        if(!chu[i])ans++;
    printf("%d\n",ans);
    return 0;
}

以上是关于缩点|强连通分量的主要内容,如果未能解决你的问题,请参考以下文章

[强连通分量+Tarjan缩点]

缩点|强连通分量

tarjan算法(强连通分量 + 强连通分量缩点 + 桥 + 割点 + LCA)

[HDOJ1827]Summer Holiday(强连通分量,缩点)

tarjan——强连通分量+缩点

tarjan强连通分量缩点模板