hdu 1269 迷宫城堡(搜索tarjin)

来源:互联网 发布:淘宝商品详情页多少钱 编辑:程序博客网 时间:2024/05/23 16:55
迷宫城堡
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 9906 Accepted Submission(s): 4444
点击打开链接
Problem Description
为了训练小希的方向感,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



题目大意就是问此图是否是连通图,就是tarjin的典型,所以tarjin是做此题的基础。(听说此题数据超级弱暴力也能过)
先来了解下tarjin算法,tarjin算法就是tarjin本人创造的一种求连通图的算法,他因此还获得过图灵奖。是基于深度优先搜索的一种求图的连通性的算法。

tarjin算法有两个重要的时间戳数组,由 low 和 dfn 来判断连通图。
dfn 数组记录当前结点的搜索时间,low 数组是记录当前搜索树中所有节点中时间最早的一个节点的搜索序号。

tarjin算法的流程:
1、搜索到的当前数组的 dfn[i] 和 low[i] 赋值为当前的时间(搜索的序号)。
2、当前搜索节点无条件入栈。
3、遍历当前结点的所有后继节点。
     ①若后继节点 j 没有搜到过(更不可能在栈中),则直接去搜索这个节点,且节点 j 的父亲节点 i 的 low[i] 值为 low[i] 和 low[j] 中较小的那个。
     ②若后继节点 j 已经搜到过,而且在栈中,说明已经搜到回路了,则节点 j 的父亲节点 i 的 low[i] 值需要为这个较早搜到节点 j 的 dfn[j] 和 low[i] 中较小的一个(因为可能当前搜索树种还能搜到时间更早的节点,则需要时 low[j] 和 dfn[i] 中较小的一个)。
4、所有后继节点结束后需要判断一下 low[i] 和 dfn[i] 的值是否相等,若相等则一个强连通图就搜完了,栈内所有元素都是属于同一个连通图。
5、继续搜索没有搜到过的节点,判断此图共有几个连通图。

套上模板这道题应该就没什么问题了。

贴上AC代码:
#include <iostream>#include <cstdio>#include <cstring>#define SIZE 100050using namespace std;struct node{    int u,v,next;}e[SIZE];int n,m;int t,head[SIZE];       //存图所用int dfn[SIZE],low[SIZE];        //dfn 记录节点搜索的时间戳、 low 记录当前搜索树能搜到的最早时间的节点的时间int Stack[SIZE];bool instack[SIZE];int stop,index,cnt;     //栈顶指针、时间戳、连通分量数目int flag[SIZE];     //记录当前节点属于哪个连通分量void join(int u,int v)      //链式前向星{    e[t].u = u;    e[t].v = v;    e[t].next = head[u];    head[u] = t++;}void tarjin(int i){    int j;    dfn[i] = low[i] = ++ index;     //修改时间    Stack[++ stop] = i;     //无条件入栈    instack[i] = true;      //标记在栈中    for(int t = head[i]; t != -1 ; t = e[t].next)       //遍历当前结点的所有子节点    {        j = e[t].v;        if(dfn[j] == -1)     //不在栈中则继续向下搜        {            tarjin(j);            low[i] = min(low[i],low[j]);        }        else if(instack[j])        {            low[i] = min(low[i],dfn[j]);        }    }    if(low[i] == dfn[i])    //表明已经回溯到了当前搜索树的根节点    {        cnt ++;        do        {            j = Stack[stop --];     //弹出栈顶            instack[j] = false;            flag[j] = cnt;      //记录弹出元素属于哪个连通分量        }while(j!=i);    }}void work(){    memset(dfn,-1,sizeof(dfn));    cnt = stop = index = 0;    for(int i = 1 ; i <= n ; i ++ )    {        if(dfn[i]==-1)            tarjin(i);    }}int main(){    freopen("in.txt","r",stdin);    while(scanf("%d%d",&n,&m)!=EOF)    {        int u,v;        if(n == 0 && m == 0)continue;        t = 0;        memset(head,-1,sizeof(head));       //注意初始化数组、因为后面的循环截至条件与此相关        for(int i = 0 ; i < m ; i ++)        {            scanf("%d%d",&u,&v);            join(u,v);        }        work();        if(cnt == 1)        {            printf("Yes\n");        }        else            printf("No\n");    }    return 0;}



0 0
原创粉丝点击