[笔记]: Tarjan算法求有向图的强连通分量

来源:互联网 发布:apache实现负载均衡 编辑:程序博客网 时间:2024/05/18 18:01

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




原创粉丝点击