• HDU 1269 迷宫城堡(向量)(Tarjan模版题)


    迷宫城堡

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 19017    Accepted Submission(s): 8328


    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结束。
     
    #include <iostream>  
    #include <cstring>  
    #include <cstdio>  
    #include <cstdlib>  
    #include<stack>
    using namespace std;  
      
    #define MAXN 10010  
    #define MAXM 100010
    stack<int>s;
    int head[MAXN],dfn[MAXN], low[MAXN], belong[MAXM];  
    int instack[10010];  // instack[]为是否在栈中的标记数组   
    int n, m, cnt, scnt, top, tot;  
    struct Edge  
    {  
          int v, next;    
    }e[MAXM];    //边结点数组   
    
    void add(int u,int v)
    {
        e[++cnt].v=v;
        e[cnt].next=head[u];
        head[u]=cnt;
    
    }
    void init()  
    {  
        cnt = 0;  
        scnt=0;    //初始化连通分量标号
        memset(head, -1, sizeof(head));  
        memset(dfn, 0, sizeof(dfn));   //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用   
        while(!s.empty()) s.pop();
    }  
    
    void Tarjan(int v) 
    {
        int min,t;
        dfn[v]=low[v]=cnt++;
        instack[v]=1;
        s.push(v);
        for(int i=head[v];i!=-1;i=e[i].next)
        {
            int j=e[i].v;
            if(!dfn[j])//未被访问 
            {
                Tarjan(j);
                // 更新结点v所能到达的最小次数层
                if(low[v]>low[j])
                    low[v]=low[j];
            }
            else if(instack[j]&&low[v]>dfn[j])
            {//如果j结点在栈内,
                low[v]=dfn[j];
            }
        }
        if(dfn[v]==low[v])
        {//如果节点v是强连通分量的根 
            scnt++;
            do
            {
                t=s.top();
                s.pop();
                instack[t]=0;
                belong[t]=scnt;
            }
            while(t!=v);
        }
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&m) && (n || m))  
        {  
            init();
            while(m--)
            {
                int u,v;
                scanf("%d%d", &u, &v); 
                add(u,v);
            }
            cnt=1;
            for(int i = 1; i <= n; i++)   //枚举每个结点,搜索连通分量  
            {
                if(!dfn[i])  //未被访问   
                {
                    Tarjan(i);  //则找i结点的连通分量 
            
                }
            }
            if(scnt == 1) printf("Yes
    ");  //只有一个强连通分量,说明此图各个结点都可达  
            else printf("No
    ");  
        }  
        return 0;  
    }
    View Code
    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
     
     
    #include <iostream>  
    #include <cstring>  
    #include <cstdio>  
    #include <cstdlib>  
    #include<stack>
    using namespace std;  
      
    #define MAXN 10010  
    #define MAXM 100010
    stack<int>s;
    int head[MAXN],dfn[MAXN], low[MAXN], belong[MAXM];  
    int instack[10010];  // instack[]为是否在栈中的标记数组   
    int n, m, cnt, scnt, top, tot;  
    struct Edge  
    {  
          int v, next;    
    }e[MAXM];    //边结点数组   
    
    void add(int u,int v)
    {
        e[++cnt].v=v;
        e[cnt].next=head[u];
        head[u]=cnt;
    
    }
    void init()  
    {  
        cnt = 0;  
        scnt=0;    //初始化连通分量标号,次序计数器,栈顶指针为0   
        memset(head, -1, sizeof(head));  
        memset(dfn, 0, sizeof(dfn));   //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用   
        while(!s.empty()) s.pop();
    }  
    
    void Tarjan(int v) 
    {
        int min,t;
        dfn[v]=low[v]=cnt++;
        instack[v]=1;
        s.push(v);
        for(int i=head[v];i!=-1;i=e[i].next)
        {
            int j=e[i].v;
            if(!dfn[j])
            {
                Tarjan(j);
                if(low[v]>low[j])
                    low[v]=low[j];
            }
            else if(instack[j]&&low[v]>dfn[j])
            {
                low[v]=dfn[j];
            }
        }
        if(dfn[v]==low[v])
        {
            scnt++;
            do
            {
                t=s.top();
                s.pop();
                instack[t]=0;
                belong[t]=scnt;
            }
            while(t!=v);
        }
    }
        
    
    
    
    int main()
    {
        while(scanf("%d%d",&n,&m) && (n || m))  
        {  
            init();
            while(m--)
            {
                int u,v;
                scanf("%d%d", &u, &v); 
                add(u,v);
            }
            cnt=1;
            for(int i = 1; i <= n; i++)   //枚举每个结点,搜索连通分量  
            {
                if(!dfn[i])  //未被访问   
                {
                    Tarjan(i);  //则找i结点的连通分量 
            
                }
            }
            if(scnt == 1) printf("Yes
    ");  //只有一个强连通分量,说明此图各个结点都可达  
            else printf("No
    ");  
        }  
        return 0;  
    }
    View Code
     1 #include <iostream>  
     2 #include <cstring>  
     3 #include <cstdio>  
     4 #include <cstdlib>  
     5 using namespace std;  
     6   
     7 #define MAXN 10010  
     8 #define MAXM 100010  
     9   
    10 struct Edge  
    11 {  
    12       int v, next;    
    13 }edge[MAXM];    //边结点数组   
    14   
    15 int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM];  
    16 // first[]头结点数组,stack[]为栈,DFN[]为深搜次序数组,Belong[]为每个结点所对应的强连通分量标号数组   
    17 // Low[u]为u结点或者u的子树结点所能追溯到的最早栈中结点的次序号   
    18 int instack[10010];  // instack[]为是否在栈中的标记数组   
    19 int n, m, cnt, scnt, top, tot;  
    20   
    21 void init()  
    22 {  
    23     cnt = 0;  
    24     scnt = top = tot = 0;    //初始化连通分量标号,次序计数器,栈顶指针为0   
    25     memset(first, -1, sizeof(first));  
    26     memset(DFN, 0, sizeof(DFN));   //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用   
    27 }  
    28   
    29 void read_graph(int u, int v) //构建邻接表   
    30 {  
    31      edge[tot].v = v;  
    32      edge[tot].next = first[u];  
    33      first[u] = tot++;  
    34 }  
    35   
    36 void Tarjan(int v)       //Tarjan算法求有向图的强连通分量   
    37 {  
    38      int min, t;  
    39      DFN[v] = Low[v] = ++tot;    //cnt为时间戳  
    40      instack[v] = 1;    //标记在栈中   
    41      stack[top++] = v;      //入栈   
    42      for(int e = first[v]; e != -1; e = edge[e].next)  
    43      {   //枚举v的每一条边   
    44            int j = edge[e].v;   //v所邻接的边   
    45            if(!DFN[j])  
    46            {   //未被访问   
    47                Tarjan(j);    //继续向下找   
    48                if(Low[v] > Low[j]) Low[v] = Low[j];  // 更新结点v所能到达的最小次数层   
    49            }  
    50            else if(instack[j] && DFN[j] < Low[v])  
    51            {   //如果j结点在栈内,   
    52                Low[v] = DFN[j];  
    53            }  
    54      }  
    55      if(DFN[v] == Low[v])  
    56      {     //如果节点v是强连通分量的根   
    57            scnt++;   //连通分量标号加1   
    58            do  
    59            {  
    60                t = stack[--top];   //退栈   
    61                instack[t] = 0;   //标记不在栈中   
    62                Belong[t] = scnt;   //出栈结点t属于cnt标号的强连通分量   
    63            }while(t != v);  //直到将v从栈中退出   
    64      }  
    65 }  
    66   
    67 void solve()  
    68 {  
    69      for(int i = 1; i <= n; i++)   //枚举每个结点,搜索连通分量  
    70         if(!DFN[i])  //未被访问   
    71            Tarjan(i);  //则找i结点的连通分量   
    72 }  
    73   
    74 int main()  
    75 {  
    76     while(scanf("%d%d",&n,&m) && (n || m))  
    77     {  
    78         init();  
    79         while(m--)  
    80         {  
    81             int u, v;  
    82             scanf("%d%d", &u, &v);  
    83             read_graph(u, v);  
    84         }  
    85         solve();     //求强连通分量   
    86         if(scnt == 1) printf("Yes
    ");  //只有一个强连通分量,说明此图各个结点都可达  
    87         else printf("No
    ");  
    88     }  
    89     return 0;  
    90 }  
    View Code
    #include<iostream>  
    #include<cstdio>  
    #include<cmath>  
    #include<cstring>  
    #include<queue>  
    #include<vector>  
    
    using namespace std;
    vector<int>gh1[100005];//向量
    vector<int>gh2[100005];
    int cnt;
    int vir[100005];
    void dfs1(int n)
    {
        int x;
        vir[n] = 1;
        cnt++;
        for (int i = 0; i<gh1[n].size(); i++)
        {
            x = gh1[n].at(i);
            if (vir[x] == 0)
                dfs1(x);
        }
    }
    void dfs2(int n)
    {
        int x;
        vir[n] = 1;
        cnt++;
        for (int i = 0; i<gh2[n].size(); i++)
        {
            x = gh2[n].at(i);
            if (vir[x] == 0)
                dfs2(x);
        }
    }
    
    
    int main()
    {
        int m, n, a, b;
        while (~scanf("%d%d", &n, &m) && (m || n))
        {
            for (int i = 0; i <= n; i++)
            {
                gh1[i].clear();
                gh2[i].clear();
            }
            for (int i = 1; i <= m; i++)
            {
                scanf("%d%d", &a, &b);
                gh1[a].push_back(b);
                gh2[b].push_back(a);
            }
            memset(vir, 0, sizeof(vir));
            cnt = 0;
            dfs1(1);//正的走一次
            if (cnt != n)
            {
                printf("No
    ");
                continue;
            }
            memset(vir, 0, sizeof(vir));
            cnt = 0;
            dfs2(1);//反的走一次
            if (cnt != n)
            {
                printf("No
    ");
                continue;
            }
            printf("Yes
    ");
    
        }
    
        return 0;
    }
  • 相关阅读:
    IOC注解开发与XML整合
    Spring的IOC注解开发入门2
    Spring的IOC注解开发入门1
    降维学习笔记
    决策树集成学习summary
    python shuffle
    为什么会突然难过
    保存为pickle
    gitlab被屏蔽问题
    selenium-历史(一)
  • 原文地址:https://www.cnblogs.com/caiyishuai/p/8410966.html
Copyright © 2020-2023  润新知