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

 

posted @ 2019-07-25 11:26  Markill  阅读(2265)  评论(0编辑  收藏  举报