在有向图中,如果两个顶点间至少存在一条路径,称两个顶点强连通。
如果有向图G的每两个顶点都强连通,称G是一个强连通图。
非强连通图有向图的极大强连通子图,称为强连通分量。
而用tarjan算法可以起求出各个强连通分量,然后再把强连通分量缩成一个点,非强连通图就被转换成一个
DAG,去多问题都是在此基础上求解。
以下是模板:
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <stack> using namespace std; #define maxn 10010 vector <int> G[maxn]; int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt; //scc_cnt为SCC的计数器,sccno[i]为i所在SCC的编号 stack <int> S; int out0[maxn]; void dfs(int u) { pre[u]=lowlink[u]=++dfs_clock; S.push(u); for(int i = 0; i < G[u].size(); i++) { int v=G[u][i]; if(!pre[v]) //如果没有访问则访问 { dfs(v); lowlink[u]=min(lowlink[u],lowlink[v]); }else if(!sccno[v]) //如果访问了且在栈中 lowlink[u]=min(lowlink[u],pre[v]); } if(lowlink[u]==pre[u]) { scc_cnt++; while(1) { int x=S.top(); S.pop(); sccno[x]=scc_cnt; if(x==u)break; } } } void find_scc(int n) { dfs_clock=scc_cnt=0; memset(sccno,0,sizeof(sccno)); memset(pre,0,sizeof(pre)); for(int i = 1; i <= n; i++)if(!pre[i])dfs(i);//节点编号从1开始 } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { for(int i = 0; i <= n; i++)G[i].clear(); for(int i = 0; i < m; i++) { int a,b; scanf("%d%d",&a,&b); G[a].push_back(b); } find_scc(n); if(scc_cnt==1) { printf("%d\n",n); continue; } memset(out0,0,sizeof(out0)); for(int u = 1; u <= n; u++) for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(sccno[u]!=sccno[v]) out0[sccno[u]]++; } int a=0,id; for(int i = 1; i <= scc_cnt; i++) if(out0[i]==0){a++;id=i;} if(a==1) { int ans = 0; for(int i = 1; i <= n; i++) if(sccno[i]==id)ans++; printf("%d\n",ans); } else { printf("0\n"); } } return 0; }