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 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; }