zoukankan      html  css  js  c++  java
  • Tarjan算法

    Tarjan是基于对图DFS的算法

    过程中遇到四种边

    树枝边:dfs搜索树上的边  满足边(u,v)  v不在栈中 u为v的父节点

    前向边:与dfs方向一致 祖先指向子孙 没什么用

    后向边:与dfs方向相反 子孙指向祖先 满足边(u,v)  v在栈中,u为v的祖先节点

    横叉边:从某个结点指向搜索树中另一子树中某结点的边 满足边(u,v)  v在栈中 u不为v的祖先节点

    主要过程

    我们主要要用到以下数组实现(没写邻接表)

    sta[top]数组模拟栈 负责加入和弹出遍历到的节点 栈的特点:先进后出

    dfn[i]表示时间戳 即为搜索次序编号

    low[i]初始值=dfn[i] 后续可能更新

    co[i]=x 表示 i在第x个强联通分量中

    对于low的更新 我们做如下操作

    我们访问到u,v与u相连

    如果(u,v)是树枝边 low[u]=min(low[u],low[v])

    如果是横叉边或后向边 low[u]=min(dfn[v],low[u])

    其实两种更新的差别就在于v是否被访问过 即dfn[v]有没有被赋值

     1 void tarjan(int u)
     2 {
     3     dfn[u]=low[u]=++num;//数据初始为时间戳
     4     sta[++top]=u;//访问到一个点就让它入栈
     5     for(int i=head[u];i;i=nxt[i])//扫描出边 
     6     {
     7         int v=ver[i];
     8         if(!dfn[v])//没有被访问过 那就是树枝边 
     9         {
    10             tarjan(v);
    11             low[u]=min(low[u],low[v]); 
    12         }
    13         else if(!co[v])//访问过 并且结点v还在栈内
    14             low[u]=min(low[u],dfn[v]); 
    15     }
    16     if(low[u]==dfn[u])//没有被更新 那么该节点即在栈中以上的结点构成一个强连通分量
    17     {
    18         co[u]=++tot;//tot记录强连通分量个数
    19         while(sta[top]!=u) 
    20         {
    21             co[sta[top]]=tot;//染色 
    22             --top;
    23         } 
    24         --top;//u退栈  
    25     }
    26 }

    例题bzoj1051: [HAOI2006]受欢迎的牛

    题目描述

      每一头牛的愿望就是变成一头最受欢迎的牛。现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎。 这
    种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎。你的任务是求出有多少头
    牛被所有的牛认为是受欢迎的。

    输入

      第一行两个数N,M。 接下来M行,每行两个数A,B,意思是A认为B是受欢迎的(给出的信息有可能重复,即有可
    能出现多个A,B)

    输出

      一个数,即有多少头牛被所有的牛认为是受欢迎的。

    样例输入

    3 3
    1 2
    2 1
    2 3

    样例输出

    1

    提示

    100%的数据N<=10000,M<=50000
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    const int MaxN=1e4+5,MaxM=5e4+5;
    int head[MaxN],ver[MaxM],nxt[MaxM],tot;
    int sta[MaxN],top;
    int co[MaxN],col;
    int dfn[MaxN],low[MaxN],num;
    int si[MaxN],n,m,de[MaxN];;
    void add(int x,int y)
    {
        ver[++tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
    }
    void tarjan(int u)
    {
        dfn[u]=low[u]=++num;
        sta[++top]=u;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=ver[i];
            if(!dfn[v]){
                tarjan(v);
                low[u]=min(low[v],low[u]);
            }
            else if(!co[v])
                low[u]=min(low[u],dfn[v]);
        }
        if(low[u]==dfn[u]){
            co[u]=++col;
            ++si[col];
            while(sta[top]!=u){
                ++si[col];
                co[sta[top]]=col;
                --top;
            }
            --top;
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1,x,y;i<=m;i++){
            scanf("%d%d",&x,&y);
            add(y,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]){
                if(co[i]!=co[ver[j]])
                    de[co[ver[j]]]++;
            }
        } 
        int ans=0,u=0;
        for(int i=1;i<=col;i++)
            if(!de[i])
                ans=si[i],u++;
        if(u==1)
            printf("%d",ans);
        else printf("0");
        return 0;
    }
    cow
  • 相关阅读:
    JAVA基础复习-输入、输出缓冲字节流整合(实现图片复制)
    JAVA基础复习一 字节输入、输出流整合(实现图片复制)
    JAVA基础复习-FileRead与FileWriter结合使用示例:字符输入、输出流整合(实现文本类文件的复制)
    JAVA基础复习- 字符流FileWriter
    JAVA基础复习-字符流FileRead
    JAVA基础复习一 File类的常用功能
    mysql设置大小写敏感
    Understanding the Settlement Mechanism in Microsoft Dynamics AX
    AX2012导入导出excel
    Changes in Microsoft Dynamics AX 2012 InventTrans data model
  • 原文地址:https://www.cnblogs.com/WJill/p/11243110.html
Copyright © 2011-2022 走看看