所谓Tarjan算法 建议学习算法竞赛入门经典训练指南中的这一章对此的介绍
因为我的解释需要中文十级才看的懂
核心就是dfs中记录一个二元组
一个叫dfn(就是算法训练入门经典中的pre)还有一个叫low
dfn就是在图中dfs时的时间戳
low则是这个点能访问回到的点的dfn的最小值
举个例子
例如
1->4<->5
此时的1 4 5 dfn值分别为123
当访问到5的时候low值都初始化为自己的dfn本身(自己可以到达自己)
因为此时5没有连别的点 dfn(5)会又搜索到4 所以此时的low[5]=min[low[5],dfn[4]]=2(原来是3)
然而4搜不到1 那么4的low值就还是dfn(4) 此时4 5是一个强连通分量
Tarjan算法中需要用一个栈 记录搜到的点 用一个instack标记每个点是否在栈中
还是上面的例子 当搜到5的时候栈里面是 1 4 5
当5 反向到4的时候 如果4 在栈中 就说明4可以搜索到5(核心)
因为5可以到4且4可以到5 那么此时4 5就是强连通分量 把4 5弹出 连通分量的数目加一
例题就是poj的2186
求出受欢迎的牛的个数
其实就是求某个出度为0的连通的元素的个数
先套模板Tarjan求强联通分量
求这个个数时 如果某个点连到了别的连通分量 那么这个点所在的连通分量都不是受欢迎的(这个连通分量的牛认为别人联通分量的牛受欢迎 那么这个组就不收欢迎了)
注意!!(WA了刚好10次。。血的教训)
1.根据题意这个出度为0的连通分量最多只有一个 如果大于1个 则直接输出0
2.还有就是此题是多组输入数据。。。。(调了一个晚上)
下面代码 虽然因为后面调试开了好多好多数组 改的不成样子了
但是Tarjan模板还是可以将就着看的
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int Stack[100005],top,num,ans=0,cnt=0,dfn[100005],low[10005],head[10005],id[10005];
int answer=0;
bool inStack[10005],failed[10005];
struct node{int to,next;}e[100005];
void insert(int x,int y){
e[++cnt].to=y;e[cnt].next=head[x];head[x]=cnt;
}
void tarjan(int x){
num++;
Stack[++top]=x;inStack[x]=1;
dfn[x]=num;low[x]=num;
for(int i=head[x];i;i=e[i].next){
int k=e[i].to;
if(!dfn[k]){
tarjan(k);
low[x]=min(low[x],low[k]);
}
else{
if(inStack[k])
low[x]=min(low[x],dfn[k]);
}
}
if(low[x]==dfn[x]){
int t=Stack[top];ans++;
while(t!=x)
{
inStack[t]=0;
id[t]=ans;
t=Stack[--top];
}
id[x]=ans;top--;
}
}
int main(){
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(head,0,sizeof(head));
memset(Stack,0,sizeof(Stack));
memset(failed,0,sizeof(failed));
memset(inStack,0,sizeof(inStack));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(id,0,sizeof(id));
top=0;num=0;
cnt=0;ans=0;answer=0;
int x,y;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
insert(x,y);
}
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=e[j].next){
int k=e[j].to;
if(id[k]!=id[i]){
failed[id[i]]=1;
}
}
}
int tmp=0;
for(int i=1;i<=ans;i++){
if(!failed[i]) tmp++;
}
if(tmp>1){
puts("0");continue;
}
for(int i=1;i<=n;i++){
if(!failed[id[i]]) answer++;
}
printf("%d\n",answer);
}
return 0;
}