强连通分量

来源:互联网 发布:淘宝赚钱方法 编辑:程序博客网 时间:2024/06/05 09:37

讲解

一道模板题

牛的舞会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 100001using 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 1500001using 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);}