<题目链接>
题目大意:
Input
输入包含多组数据,输入的第一行有两个数:N和M,接下来的M行每行有两个数a和b,表示了一条通道可以从A房间来到B房间。文件最后以两个0结束。
Output
对于输入的每组数据,如果任意两个房间都是相互连接的,输出"Yes",否则输出"No"。
Sample Input
3 3
1 2
2 3
3 1
3 3
1 2
2 3
3 2
0 0
Sample Output
Yes
No
解题分析:
有向图求强连通分量的裸题,下面用的是Targan算法。
#include <cstdio> #include <cstring> const int maxn=100000+100; struct EDGE{ int next; int to; }edge[maxn]; int n,m,cnt,top,sumlink; int head[maxn]; int dfn[maxn]; int vis[maxn],Stack[maxn]; int low[maxn]; int min(int a,int b){return a<b?a:b;} void add(int u,int v){ //链式前向星构图 edge[++cnt].next=head[u]; edge[cnt].to=v; head[u]=cnt; } void INIT(){ //初始化 memset(dfn,0,sizeof(dfn)); memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); cnt=0; //遍历的时间坐标 top=0; //栈中的元素个数 sumlink=0; //连通分量的个数 } void Targan(int u){ if(sumlink>1)return; //如果连通分量>1,说明不符合题意,直接结束 dfn[u]=low[u]=++cnt; //dfn为遍历的序号 Stack[++top]=u; //入栈 vis[u]=true; //标记该点在栈里 for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(!dfn[v]){ //如果这个点没有遍历过 Targan(v); low[u]=min(low[u],low[v]); } else if(vis[v]){ //如果这个点在栈中 low[u]=min(low[u],dfn[v]); } } if(dfn[u]==low[u]){ //如果找到了这个连通分量的根节点 sumlink++; //连通分量数量+1 while(true){ int temp=Stack[top]; vis[temp]=0; --top; //及时将该连通块在栈中的点排出,配合vis[]数组,能够使同一连通块的low值相同,而不是这个联通块的值等于它之前遍历到的连通块的low值 if(temp==u) //将该连通块中的根节点搜索完后(根节点==该连通块最早被dfs到的点==该连通块在栈的最底部的点) break; } } } int main(){ while(scanf("%d %d",&n,&m)!=EOF,n|m){ INIT(); for(int i=1;i<=m;i++){ int u,v; scanf("%d %d",&u,&v); add(u,v); } for(int i=1;i<=n;i++){ if(!dfn[i]) Targan(i); } if(sumlink==1){ printf("Yes\n"); } else printf("No\n"); } return 0; }
2018-08-16
原文:https://www.cnblogs.com/00isok/p/9489930.html