判断一个有向图是不是仙人掌图。即强连通且每条边只属于一个环。
利用tarjan求强连通的同时找环。当某个顶点被第二次访问时,说明有环存在。
即访问某个顶点的时候,记录该顶点的上一个节点,每次找到一个环,就将该环上的所有顶点度数+1,如果某个顶点度数超过1,则说明其属于两个环。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<stack> #include<vector> #pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; #define maxn 20005 vector<int> G[maxn]; int pre[maxn],low[maxn],dfs_clock,scc_cnt,fa[maxn],in[maxn]; bool Find(int x,int a) { while(fa[x]!=a){ in[x]++; if(in[x]>1) return 0; x=fa[x]; } return 1; } bool dfs(int u){ pre[u]=low[u]=++dfs_clock; for(int i=0;i<(int)G[u].size();++i){ int v=G[u][i]; if(!pre[v]){ fa[v]=u; if(!dfs(v)) return 0; low[u]=min(low[u],low[v]);//用后代的low函数更新自身 } else{ low[u]=min(low[u],pre[v]);//用反向边更新 if(!Find(u,v)) return 0; } } if(low[u]==pre[u]){ ++scc_cnt; if(scc_cnt>1) return 0; } return 1; } bool find_scc(int n){ dfs_clock=scc_cnt=0; memset(pre,0,sizeof(pre)); memset(in,0,sizeof(in)); for(int i=0;i<n;++i) if(!pre[i]) if(!dfs(i)) return 0; return 1; } int main() { int t,i,x,y,n; cin>>t; while(t--) { scanf("%d",&n); for(i=0;i<n;++i) G[i].clear(); while(scanf("%d%d",&x,&y)&&(x+y)) G[x].push_back(y); if(find_scc(n)&&scc_cnt==1) puts("YES"); else puts("NO"); } return 0; }