3 3 1 2 2 3 3 1 3 3 1 2 2 3 3 2 0 0
Yes No
先学习最简单的强连通分量的算法:Kosaraju算法
Kosaraju算法虽然需要两次dfs并对图进行转置,但是似乎能在线性时间求出DAG的一个拓扑排序
理解算法后(证明太长没怎么看...):
①对原图进行dfs(注意图可能不连通),求出遍历的点的离开时间(即在离开时入栈)
②对转置图进行dfs,起始点从离开时间倒序枚举(即对第一次dfs形成的栈出栈),然后强连通分量+1
③若还存在没有遍历的点(即栈中还存在未遍历的点),重复②
手敲一遍,感觉还差不多
#include <cstdio> #include <cstring> #include <vector> using namespace std; int n,m,s,e; int stak[10005],top;//top指向栈顶元素的后一个,也表示栈内元素个数 vector<int> edge[10005],edge_T[10005]; bool vis[10005]; void dfs(int u) { vis[u]=true; for(int i=0;i<edge[u].size();++i) { if(!vis[edge[u][i]]) dfs(edge[u][i]); } stak[top++]=u; } void dfs_T(int u) { vis[u]=true; for(int i=0;i<edge_T[u].size();++i) { if(!vis[edge_T[u][i]]) dfs_T(edge_T[u][i]); } } int Kosaraju() { int cnt=0;//强连通分支个数 top=0; memset(vis,false,sizeof(vis)); for(int i=1;i<=n;++i)//防止图不连通 if(!vis[i]) dfs(i); memset(vis,false,sizeof(vis)); while(top>0) { s=stak[--top]; if(!vis[s]) { dfs_T(s); ++cnt;//强联通连通分支数+1 } } return cnt; } int main() { while(scanf("%d%d",&n,&m),n!=0||m!=0) { for(int i=1;i<=n;++i) { edge[i].clear(); edge_T[i].clear(); } while(m-->0) { scanf("%d%d",&s,&e); edge[s].push_back(e); edge_T[e].push_back(s); } printf("%s\n",Kosaraju()==1?"Yes":"No"); } return 0; }