迷宫城堡 【求SCC 个数】

来源:互联网 发布:网络应用安全包括 编辑:程序博客网 时间:2024/05/18 00:48

为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在一条路径可以从房间i到房间j,也存在一条路径可以从房间j到房间i。
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
Hint

学习了,第一道 SCC
对于 SCC 的tarjan算法的学习理解,可以参考

http://www.cnblogs.com/uncle-lu/p/5876729.html

还有这个

http://m.blog.csdn.net/article/details?id=16361033

代码

#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>#include<cmath>#include<queue>#include<stack>#include<map>#include<vector>#include<set>#define CLR(a,b) memset((a),(b),sizeof(a))#define inf 0x3f3f3f3f#define mod 100009#define LL long long#define M  100000+100#define ll o<<1#define rr o<<1|1#define lson o<<1,l,mid#define rson o<<1|1,mid+1,rusing namespace std;void read(int &x){    x=0;char c;    while((c=getchar())<'0');    do x=x*10+c-'0';while((c=getchar())>='0');}struct Edge {    int from,to,next;}edge[M];int head[M],top;  // 向前星存图 int dfs_clock;// 时间戳int sccno[M];//sccno[i] 表示 i是属于哪个 SCC ;int scc_cnt;// scc_cnt 表明有几个 SCC bool Instack[M];// 该点是不是字栈中int low[M];  // 以当前i为父节点的子树 能够连接到栈中最上面的点的 DFN 的值(能连接到的最小的dfn值) int dfn[M];//作为这个点搜索的次序编号(时间戳),就是第几个被搜索到的;(也可以说是深度) int n,m;stack<int>S;  // 存储整个强联通分量 void init(){    memset(Instack,false,sizeof(Instack));    memset(low,0,sizeof(low));    memset(dfn,0,sizeof(dfn));    memset(sccno,0,sizeof(sccno));    memset(head,-1,sizeof(head));    top=scc_cnt=dfs_clock=0;  } void addedge(int a,int b){    Edge e={a,b,head[a]};    edge[top]=e;head[a]=top++;}void getmap(){    int a,b;    while(m--)    {        read(a);read(b);        addedge(a,b);    }}void tarjan(int now,int fa){    int nexts;    int i,j;    low[now]=dfn[now]=++dfs_clock;    S.push(now);    Instack[now]=true;    for(i=head[now];i!=-1;i=edge[i].next)    {        Edge e=edge[i];        if(!dfn[e.to])  // 没有搜索到过         {            tarjan(e.to,now);            low[now]=min(low[now],low[e.to]);        }         else if(Instack[e.to])  //  已经搜索到过,并且还在 栈中, 当前点和在栈中的点有父子关系         low[now]=min(low[now],dfn[e.to]);      }        if(low[now]==dfn[now]) // 表明 这个节点是当前这个强联通分量的 根节点(因为这个low是这个强联通分量里最小的)         {            scc_cnt++;            for(;;)  //将 此节点 以及 比此节点后进来的 节点都出栈,出栈的所有节点 就构成了一个 全新的强联通分量             {   nexts=S.top(); S.pop();                Instack[nexts]=false;                if(nexts==now) break;            }         } }void find_cut(int l,int r)  // 询问 l到r顶点  是不是在同一个SCC中 {    for(int i=l;i<=r;i++)    if(!dfn[i]) tarjan(i,-1);    puts(scc_cnt==1?"Yes":"No"); }int main(){    while(scanf("%d%d",&n,&m)&&(n||m))    {        init();        getmap();        find_cut(1,n);    }    return 0;}
0 0