强连通分量

讲解

一道模板题

牛的舞会The Cow Prom
#include<iostream>
#include<cstdio>

using namespace std;
int n,m,to[1000001],nxt[100001],head[1000001];
int edge,dfn[100001],low[100001],xx,yy,ans,top,time;
bool in[1000001];
int duilie[100001],mmp,pc;
void tarjan(int pccc)
{
    dfn[pccc]=low[pccc]=++time;
    duilie[++top]=pccc;
    in[pccc]=1;
    for(int i=head[pccc];i;i=nxt[i])
    {
        int t=to[i];
        if(!dfn[t])//没有走到过
        {
            tarjan(t);  
            if(low[t]<low[pccc]) 
            low[pccc]=low[t];
        }
        else//这个点之前已经走过了,并且是当前节点的祖先
        {
            if(low[pccc]>dfn[t]&&in[t]==1)
            low[pccc]=dfn[t];//用t的dfn值更新,low值好像也行。。
        }

    }
    if(dfn[pccc]==low[pccc])//出栈
    {
        pc=0;
        while(duilie[top]!=pccc)
        {
            in[duilie[top]]=0;
            top--;
            pc++;           
        }
        top--;
        in[duilie[top]]=0;
        if(pc>=1) ans++;
    }
}
int main()
{
//  freopen("1.txt","r",stdin);
    cin>>n>>m;
    for(int i=1;i<=m;i++)//建图
    {
        scanf("%d%d",&xx,&yy);
        edge++;
        to[edge]=yy;
        nxt[edge]=head[xx];
        head[xx]=edge;
    }
    for(int i=1;i<=n;i++)//有些点是不连通的
    {
        if(!dfn[i])
        tarjan(i);
    }
    printf("%d",ans);
}

洛谷上一个大神写的,我和他的思路很像qwq
感觉他写的更好。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100001
using namespace std;
bool vis[N];
int n,m,x,y,tim,tot,top,sum;
int head[N],dfn[N],low[N],stack[N],belong[N];
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct Edge
{
    int from,next,to;
}edge[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int tarjan(int now)
{
    dfn[now]=low[now]=++tim;
    stack[++top]=now;vis[now]=true;
    for(int i=head[now];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(vis[t]) low[now]=min(low[now],dfn[t]);
        else if(!dfn[t]) tarjan(t),low[now]=min(low[now],low[t]);
    }
    if(low[now]==dfn[now])
    {
        sum++;belong[now]=sum;
        int ans=1;
        for(;stack[top]!=now;top--)
        {
            vis[stack[top]]=false;
            belong[stack[top]]=sum;
            ans++;
        }
        vis[now]=false;top--;
        if(ans==1) sum--;
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
     x=read(),y=read(),add(x,y);
    for(int i=1;i<=n;i++)
      if(!dfn[i]) tarjan(i);
    printf("%d",sum);
    return 0;
}

缩点

刻录光盘

应该是个假缩点。。
tarjan一遍后遍历每个点和这个点连的所有边,记录好每一个点的belong,统计每一个强连通分量的入度,入度为0,ans++。qwq

我觉得我这个代码比上边的好。。

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,nxt[1000001],head[1000001],to[1000001],x,y,edge,time,belong[1000001],cnt,top;
int stack[100001],ru[1000001],ans,dfn[1000001],low[1000001],pc;
bool in[100001];
void add(int x,int y)
{
    edge++;
    to[edge]=y;
    nxt[edge]=head[x];
    head[x]=edge;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++time;
    in[x]=1;stack[++top]=x;
    for(int i=head[x];i;i=nxt[i])
    {
        int t=to[i];
        if(!dfn[t])
        {
            tarjan(t);
            low[x]=min(low[x],low[t]);
        }
        else
        {
            if(in[t]) low[x]=min(low[x],dfn[t]);
        }
    }
    if(dfn[x]==low[x])
    {
        cnt++;
        do
        {
            pc=stack[top];
            belong[pc]=cnt;
            in[pc]=0;
            top--;
        }
        while(pc!=x);//非常容易出错的细节,这里的pc是上一个stack[top],而之后的一步top--,可以拿只有一个点的强连通分量模拟
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        while(scanf("%d",&x)&&x)
        {
            add(i,x);
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i]) tarjan(i);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=head[i];j;j=nxt[j])
        {
            int t=to[j];
            if(belong[t]!=belong[i]) ru[belong[t]]++;
        }
    }   
    for(int i=1;i<=cnt;i++)
    {
        if(ru[i]==0) ans++;
    }
    printf("%d",ans);
}
消息扩散

和上题一样,一模一样。

#include<iostream>
#include<cstdio>
#define maxn 1500001
using namespace std;
int n,m,to[600001],nxt[600001],head[600001],dfn[maxn],low[maxn],belong[maxn],ru[maxn];
bool in[maxn];
int e,xx,yy,stack[600001],top,time,pc,cnt,ans;
void tarjan(int x)
{
    stack[++top]=x;
    dfn[x]=low[x]=++time;
    in[x]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        int t=to[i];
        if(!dfn[t]) 
        {
            tarjan(t);
            low[x]=min(low[t],low[x]);
        }
        else 
        {
            if(in[t])
            {
                low[x]=min(low[x],dfn[t]);
            }
        }
    }
    if(dfn[x]==low[x])
    {
        cnt++;
        do
        {
            pc=stack[top];
            belong[pc]=cnt;
            top--;
            in[pc]=0;
        }
        while(pc!=x);
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&xx,&yy);
        e++;
        to[e]=yy;
        nxt[e]=head[xx];
        head[xx]=e;
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i]) tarjan(i);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=head[i];j;j=nxt[j])
        {
            int t=to[j];
            if(belong[t]!=belong[i]) 
            ru[belong[t]]++;
        }
    }
    for(int i=1;i<=cnt;i++)
    {
        if(ru[i]==0) ans++; 
    }
    printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值