• 求图的强连通分量--tarjan算法


    一:tarjan算法详解

    ◦思想:
    ◦做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点DFS过程中i的下方节点所能到达的开始时间最早的节点的开始时间。(也就是之后的深搜所能到达的最小开始时间)初始时dfn[i]=low[i]
    ◦在DFS过程中会形成一搜索树。在搜索树上越先遍历到的节点,显然dfn的值就越小。
    ◦DFS过程中,碰到哪个节点,就将哪个节点入栈。栈中节点只有在其所属的强连通分量已经全部求出时,才会出栈。
    ◦(对于一个强连通分量和一个指出去的有向边,在输出这个强连通分量的时候,那条指出去的边的终点已经作为一个强连通分量统计,并且出栈了。)
    ◦如果发现某节点u有边连到搜索树(栈)中栈里的节点v,则更新u的low 值为dfn[v](更新为low[v]也可以,更新为low可以算是压缩路径,速度略快)。
    ◦如果一个节点u已经DFS访问结束,而且此时其low值等于dfn值,则说明u可达的所有节点,都不能到达任何在u之前被DFS访问的节点 ---- 那么该节点u就是一个强连通分量在DFS搜索树中的根。
    ◦此时将栈中所有节点弹出,包括u,就找到了一个强连通分量
    模板:void Tarjan(u) {/*注意小写的tarjan是关键字,还有index也是*/    dfn[u]=low[u]=++index    stack.push(u)
    for each (u, v) in E {
            if (v is not visted) {
                tarjan(v) 
                low[u] = min(low[u], low[v]) 
            }
            else if (v in stack) {
                low[u] = min(low[u], dfn[v]) /*这里是low[u] = min(low[u], low[v])也是可以的,因为如果这个点被访问了,而且仍然待在栈中,说明u---v是一条回边,那么这里用dfn[v]
    low[v]都是相同了的*/
    }
    } if (dfn[u] == low[u])
    { //u是一个强连通分量的根 repeat v = stack.pop
    print v
    until (u== v) } //退栈,把整个强连通分量都弹出来 } //复杂度是O(E+V)的

    例题:1001. [WZOI2011 S3] 消息传递(来源sojs.tk)

    ★★   输入文件:messagew.in   输出文件:messagew.out   简单对比
    时间限制:1 s   内存限制:128 MB

    Problem 2 消息传递 (messagew.pas/c/cpp)
    问题描述
    WZland开办了一个俱乐部(这里面可以干任何的事情),这引来了许多的人来加入。俱乐部的人数越来越多,关系也越来越复杂……
    俱乐部的人来自各个地方,为了增加友谊,俱乐部举行了一次晚会。晚会上又进行了一个传话游戏,如果A认识B,那么A收到某个消息,就会把这个消息传给B,以及所有A认识的人(如果A认识B,B不一定认识A),所有人从1到N编号。
    现在给出所有“认识”关系,俱乐部的负责人WZland的国王想知道一个十分简单的问题:如果A发布一条新消息,那么会不会经过若干次传话后,这个消息传回给了A,1≤A≤N。但是WZland的国王是出了名的数学差,幸好的是你在他的身边,于是他就将这个问题交给你来解决。
    输入格式
    输入数据中的第一行是两个数N和M,两数之间有一个空格,表示人数和认识关系数。
    接下来的M行,每行两个数A和B,表示A认识B(1A, BN,AB)。
    输出格式
    输出文件中一共有N行,每行一个字符“T”或“F”。第i行如果是“T”,表示i发出一条新消息会传回给i;如果是“F”,表示i发出一条新消息不会传回给i。
    样例输入输出
    message.in 
    4 6
    1 2
    2 3
    4 1
    3 1
    1 3
    2 3
    message.out
    T
    T
    T
    F
    数据规模
    对于30%的数据,N≤1000,M≤20000;
    对于50%的数据,N≤10000,M≤100000;
    对于100%的数据,N≤100000,M≤200000;
    认识关系可能会重复给出。
    时间限制
    1s
    解析:”认识关系可能会重复给出。“根本不用处理,不会影响答案的。
    代码及其分析:
    #include<iostream>
    using namespace std;
    #include<cstdio>
    #define N 100100
    #include<vector>
    vector<int>G[N];
    vector<int>ans[N];
    int clac=0;
    bool inzhan[N];
    #include<stack>
    #include<cstring>
    stack<int>s;
    int low[N],dfn[N];
    bool flag[N]={0},ok[N]={0};
    int n,m;
    int Index;
    void input()
    {
        Index=0;
        scanf("%d%d",&n,&m);
        int a,b;
        for(int i=1;i<=m;++i)
        {
            scanf("%d%d",&a,&b);
            G[a].push_back(b);
        }
        memset(inzhan,false,sizeof(inzhan));
        memset(dfn,-1,sizeof(dfn));
        memset(low,-1,sizeof(low));
    }
    void Tarjan(int k)
    {
        int  j;
        dfn[k]=low[k]=++Index;/*tarjan过程,记录到达该点的时间,和从该点出发能到达的时间最小的点*/
        inzhan[k]=true;/*入栈*/
        s.push(k);
        for(int i=0;i<G[k].size();++i)
        {
            int tp=G[k][i];/*邻接表储存,G[k][i]表示从k点出发的第i条边的终点编号*/
            if(low[tp]==-1)
            {
                Tarjan(tp);
                low[k]=min(low[k],low[tp]);
            }
            else if(inzhan[tp])
            low[k]=min(low[tp],low[k]);
            /*整理总共有三种情况:未被访问,访问了但是是不是强连通分量还不知道(也就是仍在栈中),访问了已经出栈了(这种不用处理,因为如果这个点在好几个强连通分量中,那么他此时一定没有出栈。因为深搜嘛)*/
        }
        if(low[k]==dfn[k])/*low[k]==dfn[k]是这个强连通分量的标志,也就是起始位置,不能再向上找了*/
        {
            int l;
            clac++;/*强连通分量个数*/
            do{
            l=s.top();
            s.pop();
            ans[clac].push_back(l);
            inzhan[l]=false;
            }while(l!=k);
           if(ans[clac].size()>1)
           flag[clac]=true;/*记录这个强连通分量中的所有点是可以传话成功的,下面在重标记每一个点*/
        }
    }
    void OUT()
    {
        for(int i=1;i<=clac;++i)
        if(flag[i])
        {
            for(int j=0;j<ans[i].size();++j)
            ok[ans[i][j]]=true;
        }
        for(int i=1;i<=n;++i)
        if(ok[i])
        printf("T
    ");
        else printf("F
    ");
    }
    int main()
    {
        freopen("messagew.in","r",stdin);
        freopen("messagew.out","w",stdout);
        input();
        for(int i=1;i<=n;++i)
        if(dfn[i]==-1)
        Tarjan(i);
        OUT();
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    View Code
  • 相关阅读:
    教你怎样做个有“钱”途的測试project师
    使用具体解释及源代码解析Android中的Adapter、BaseAdapter、ArrayAdapter、SimpleAdapter和SimpleCursorAdapter
    html-上左右布局方式---ShinePans
    POJ1502 MPI Maelstrom Dijkstra
    Angry IP Scanner 获取设备的IP
    【Struts2学习笔记(12)】Struts2国际化
    使用SQL Profile及SQL Tuning Advisor固定运行计划
    sage开发url替换字符串
    柯塔娜大合唱,互联网安全观
    vim水平摆放全部窗体的三个方法
  • 原文地址:https://www.cnblogs.com/c1299401227/p/5402414.html
Copyright © 2020-2023  润新知