在有向图中,若是2个顶点之间存在至少一条路径,则称这2个顶点强连通。若是有向图G中任意2个顶点都强连通,则称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通份量。 强连通份量的求法分为主流的2种,一种是Kosaraju,作2次DFS。另一种就是伟大的计算机科学家Tarjan发明的算法,该算法只须要作一次DFS便可,比Kosaraju更快。 网上关于Tarjan算法的介绍不少,我推荐Byvoid大牛写的: 有向图强连通份量的Tarjan算法 这篇文章被wiki推荐,很是经典,看完秒懂php
一.如何求强连通份量
下面咱们先来作一道模板题:c++
HDU1269 --- 迷宫城堡 分析:Tarjan模板题,看整张图是否是一张强连通图算法
#include "bits/stdc++.h" using namespace std; const int maxn=1e5+100; struct Edge{ int to,next; }edge[maxn]; int n,m,tot,head[maxn]; int low[maxn],dfn[maxn],num[maxn],s[maxn],belong[maxn]; bool Instack[maxn]; int Index,scc,top; void init(){ tot=0; memset(head,-1,sizeof(head)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(num,0,sizeof(num)); memset(s,0,sizeof(s)); memset(belong,0,sizeof(belong)); memset(Instack,false,sizeof(Instack)); Index=scc=top=0; } void add(int u,int v){ edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++; } void Tarjan(int u){ int v; dfn[u]=low[u]=++Index; s[top++]=u; Instack[u]=true; for(int i=head[u];i!=-1;i=edge[i].next){ v=edge[i].to; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); }else if(Instack[v]){ low[u]=min(low[u],dfn[v]); } } if(low[u]==dfn[u]){ scc++; do{ v=s[--top]; Instack[v]=false; belong[v]=scc; num[scc]++; }while(u!=v); } } int main() { while(scanf("%d%d",&n,&m)!=EOF){ if(n==0&&m==0) break; init(); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); add(x,y); } for(int i=1;i<=n;i++){ if(!dfn[i]) Tarjan(i); } if(scc==1) printf("Yes\n"); else printf("No\n"); } return 0; }
二.Tarjan缩点
其实缩点也是运用Tarjan求强连通份量的方法,不过对于一些贡献具备传导性的问题有时候须要缩点,好比友情传递、路上权值等。 缩点的思想也很显然,由于强连通份量中的每两个点都是强连通的,故能够将一个强连通份量当作一个超级点,而点权按题意来定。 以下图所示就是一个缩点的例子 spa
一样关于缩点问题,咱们来看一道简单的模板题code
BZOJ1051 --- [HAOI2006]受欢迎的牛 分析:首先对于一个强连通份量里面的全部点都知足条件,因而咱们对图进行缩点,这样咱们获得的全部点都不是强连通的,如今整张图就是一个DAG。咱们考虑出度为0的点,则在图中至少存在一个出度为0的点,若是超过1个,则必不可能知足条件,不然这个点就知足条件。这个点极可能为全部强连通份量构成的超级点,因此也就是要求的也就是这个强连通份量中点的个数blog
#include "bits/stdc++.h" using namespace std; const int maxn=5e4+10; vector<int>g[maxn]; int n,m; int low[maxn],dfn[maxn],s[maxn],num[maxn],belong[maxn]; bool Instack[maxn]; int Index,scc,top; void Tarjan(int u){ int v; low[u]=dfn[u]=++Index; s[top++]=u; Instack[u]=true; for(int i=0;i<g[u].size();i++){ v=g[u][i]; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); }else if(Instack[v]){ low[u]=min(low[u],dfn[v]); } } if(low[u]==dfn[u]){ ++scc; do{ v=s[--top]; Instack[v]=false; belong[v]=scc; num[scc]++; }while(u!=v); } } int cnt[maxn],du[maxn]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); g[x].push_back(y); } for(int i=1;i<=n;i++){ if(!dfn[i]) Tarjan(i); } for(int i=1;i<=n;i++){ for(int j=0;j<g[i].size();j++){ int v=g[i][j]; if(belong[i]!=belong[v]){ du[belong[i]]++; } } cnt[belong[i]]++; } int tmp=0,ans=0; for(int i=1;i<=scc;i++){ if(du[i]==0){ tmp++; ans=cnt[i]; } } if(tmp>1) printf("0\n"); else printf("%d\n",ans); return 0; }