讲题(1st)(1)

Tarjan算法


强连通分量(scc)

    在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected component)。

Tarjan是用来求scc的

    Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。 
#include<cstdio>
#include<stack>
using namespace std;

stack <int> s;
int n,m,total,step;
struct E{
    int next,to;
}edge[2000000];
int head[2000000],edge_num;
int belong[2000000],DFN[2000000],LOW[2000000];
bool instack[2000000];

void addedge(int x,int y){
    edge[++edge_num].next=head[x];
    edge[edge_num].to=y;
    head[x]=edge_num;
}

void Tarjan(int x){
    int i;
    s.push(x);instack[x]=1;
    DFN[x]=LOW[x]=++step;
    for(i=head[x];i;i=edge[i].next){
        if(!DFN[edge[i].to]){
            Tarjan(edge[i].to);
            if(LOW[x]>LOW[edge[i].to])
                LOW[x]=LOW[edge[i].to];
        }
        if(instack[edge[i].to] && DFN[edge[i].to]<LOW[x])
            LOW[x]=DFN[edge[i].to];
    }
    if(DFN[x]==LOW[x]){
        int t;total++;
        do{
            t=s.top();s.pop();instack[t]=0;
            belong[t]=total;
        }
        while(t!=x);
    }
}

void solve(){
    int i;
    for(i=1;i<=n;i++){
        if(!DFN[i])
        Tarjan(i);
    }
}

void out(){
    int i;
    for(i=1;i<=n;i++){
        printf("Dot No.%d is in No.%d scc\n",i,belong[i]);
    }
}

int main(){
    freopen("tarjan.in","r",stdin);
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        int f,t;
        scanf("%d%d",&f,&t);
        addedge(f,t);
    }
    solve();
    out();
    return 0;
}

有两个关键数组DFN、LOW

DFN[i]:第i号节点被DFS到的序号;
LOW[i]:第i号节点DFS过程中能到达的DFN值最小的节点;
所以第一次访问到时,LOW与DFN被一起赋为step值;

被DFS到的点将被放入栈中,并记录该点在栈中;
如果DFS到了一个为被访问的点,继续DFS并更新LOW值
//该点意为 本次被DFS到的点 edge[i].to
更新该点时 比较的自然是先前该点的LOW值与本次找到该点的LOW值的较小值,即
    if(LOW[x]>LOW[edge[i].to])
                LOW[x]=LOW[edge[i].to];
else 如果DFS到了已经在栈中的点(DFS过且不再栈中就不考虑了)
如果 该点DFN值 比 栈中的点LOW值小 更新LOW[该点]=LOW[栈中点]意思就是说找到环了,将环上的LOW值更新一下,就算到这里看起来LOW好像也并没有什么用

最后一步:退栈

这里LOW数组发挥作用

    if(DFN[x]==LOW[x]){
        int t;total++;
        do{
            t=s.top();s.pop();instack[t]=0;
            belong[t]=total;
        }
        while(t!=x);
    }
从栈顶取出一个数,直到取出的这个数是起始点(LOW[x]==DFN[x])就结束了

洛谷P2746校园网

#include<cstdio>
#include<stack>
#define MAX(a,b) a>b?a:b
using namespace std;

bool instack[2000];
int belong[2000],total,inDegree[2000],outDegree[2000];
stack<int> s;
int n,head[2000],edge_num,way,TaskA,TaskB;
struct E{
    int from,next,to;
}edge[2000];
int LOW[2000],DFN[2000];

void addedge(int x,int y){
    edge[++edge_num].next=head[x];
    edge[edge_num].from=x;
    edge[edge_num].to=y;
    head[x]=edge_num;
}

void Tarjan(int x){
    int i;
    s.push(x);instack[x]=true;
    DFN[x]=LOW[x]=++way;
    for(i=head[x];i;i=edge[i].next){
        if(!DFN[edge[i].to]){
            Tarjan(edge[i].to);
            if(LOW[edge[i].to]<LOW[x]){
                LOW[x]=LOW[edge[i].to];
            }
        }
        else if(instack[edge[i].to] && DFN[edge[i].to]<LOW[x]){
            LOW[x]=DFN[edge[i].to];
        }
    }

    if(DFN[x]==LOW[x]){
        int t;
        total++;
        belong[x]=total;
        do{
            t=s.top();
            belong[t]=total;
            s.pop();
            instack[t]=false;
        }
        while(t!=x);
    }
}

void solve(){
    int i;
    for(i=1;i<=n;i++){
        if(!DFN[i])
            Tarjan(i);
    }
    for(i=1;i<=edge_num;i++){
        if(belong[edge[i].from]!=belong[edge[i].to]){
            inDegree[belong[edge[i].to]]++;
            outDegree[belong[edge[i].from]]++;
        }
    }
    for(i=1;i<=total;i++){
        if(inDegree[i]==0)
            TaskA++;
        if(outDegree[i]==0)
            TaskB++;
    }
    TaskB=MAX(TaskA,TaskB);
    if(total==1){
        printf("1\n0");return;
    }
    if(TaskA==0) TaskA=1;
    printf("%d\n%d",TaskA,TaskB);
}

int main(){
    scanf("%d",&n);
    int i,k;
    for(i=1;i<=n;i++){
        scanf("%d",&k);
        while(k){
            addedge(i,k);
            scanf("%d",&k);
        }
    }
    solve();
    return 0;
}

洛谷P2341受欢迎的牛

#include<cstdio>
#include<stack>
using namespace std;

struct edge{
    int to,next,from;
}edges[500000];
int edge_num,head[500000];//bianbiao

int n,m;  //in
int DFN[200000],LOW[200000];//Tarjan
bool instack[200000];//Tarjan
int Belong[200000],BMember[200000];//Tarjan
int tot;//Tarjan
int Outdegree[200000];
int steps=0,q;

stack<int> s;

void Tarjan(int x){
    DFN[x]=LOW[x]=++steps;
    instack[x]=1;
    s.push(x);
    int i;
    for(i=head[x];i!=0;i=edges[i].next){
        if(!DFN[edges[i].to]){
            Tarjan(edges[i].to);
            if(LOW[edges[i].to]<LOW[x]){
                LOW[x]=LOW[edges[i].to];
            }
        }
        else if(instack[edges[i].to]&&LOW[x]>DFN[edges[i].to]){
            LOW[x]=DFN[edges[i].to];
        }
    }
    if(DFN[x]==LOW[x]){
        tot++;
        do{
            q=s.top();
            s.pop();
            Belong[q]=tot;
            BMember[tot]++;
            instack[q]=false;
        }
        while(q!=x);
    }
}

void addedge(int x,int y){
    edges[++edge_num].next=head[x];
    edges[edge_num].to=y;
    edges[edge_num].from=x;
    head[x]=edge_num;
}

int main(){
    //freopen("2341.txt","r",stdin);
    scanf("%d%d",&n,&m);
    int i;
    for(i=1;i<=m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        addedge(a,b);
    }
    for(i=1;i<=n;i++){
        if(!DFN[i])
            Tarjan(i);
    }
    for(i=1;i<=m;i++){
        if(Belong[edges[i].from]!=Belong[edges[i].to]){
            Outdegree[Belong[edges[i].from]]++;
        }
    }
    int zero=0,ans;
    for(i=1;i<=tot;i++){//search every Tarjan
        if(Outdegree[i]==0){
            zero++;
            ans=i;
        }
    }
    if(zero==1) printf("%d",BMember[ans]);
    else printf("0");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值