• poj 2186 Popular Cows 【强连通分量Tarjan算法 + 树问题】


    Popular Cows
    Time Limit: 2000MS   Memory Limit: 65536K
    Total Submissions: 27496   Accepted: 11059

    Description

    Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
    popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.

    Input

    * Line 1: Two space-separated integers, N and M

    * Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.

    Output

    * Line 1: A single integer that is the number of cows who are considered popular by every other cow.

    Sample Input

    3 3
    1 2
    2 1
    2 3
    

    Sample Output

    1
    

    Hint

    Cow 3 is the only cow of high popularity.
     
    题目:样例解释,3头牛,3条边。 1崇拜2, 2崇拜1, 2崇拜3。  3号牛能获得出自己之外的所有牛的崇拜,这样的牛有多少?输出数量。
    注意:崇拜的关系是可以往上层传递的。1->2, 2->3, 可以得到:1->3.
     
    通过Tarjan算法,我们会把所有的节点划分到不同的枪连通分量中。根据强连通分量的定义,处在同一个强连通分量中的节点,一会是互相仰慕的.
    把每个强连通分量看成是一个节点。这样就构造成一棵树(树边是有向的),看这个树有几个出度为0的强连通分量。如果仅有一个,则会存在正确
    答案。否则不可能存在满足题目的可能,只能输出0。
     
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <algorithm>
    #define N 10000+10
    #define M 51000+10
    
    using namespace std;
    int n, m;
    struct node
    {
        int to;
        int next;
    }edge[M];//保证存下所有的边
    
    int head[N], dfn[N], low[N], vis[N];
    int stack[N], num[N], du[N];
    int counter, top, cut, cnt;
    /*
      dfn[]:记录搜索时各顶点的访问次序,即深度优先数
      low[u]:每个节点定义一个low值 low[u]是从u或u的子孙出发通过回边可以到达
             的最低深度优先数
      注意:dfn[]在搜索前进时进行统计 low[]值是在回退的时候进行计算
      du[]:表示出度
    */
    void init()
    {
        memset(head, -1, sizeof(head));
        memset(vis, 0, sizeof(vis));
        memset(dfn, 0, sizeof(dfn));
        memset(low, 0, sizeof(low));
        memset(num, 0, sizeof(num));
        memset(du, 0, sizeof(du));
        counter=1;//begin 1
        cnt=0;//边 计数
        top=0;// stack top
        cut=0;
    }
    
    void addedge(int u, int v)
    {
        edge[cnt].to=v;
        edge[cnt].next=head[u];
        head[u]=cnt++;
    }//静态数组 倒着建边
    
    void dfs(int u)
    {
        dfn[u]=low[u]=counter++;//counter的值从1开始
        vis[u]=1;//标记访问
        stack[top++]=u;
        //遍历从u节点发出的边
        for(int i=head[u]; i!=-1; i=edge[i].next){
            int v=edge[i].to;
            if(!vis[v]){//这是一条树边
                dfs(v);
                low[u]=min(low[u], low[v]);//回溯的时候比较
            }
            else if(vis[v]){
                low[u]=min(low[u], dfn[v]);
            }
        }
        if(low[u]==dfn[u]){
            cut++;//表示已经找到了一个强连通分量
            while(top>0 && stack[top]!=u){
                top--;
                vis[stack[top]]=2;//
                num[stack[top]]=cut;//确定当前这个节点属于哪个强连通分量
            }
        }
    }
    
    int main()
    {
        int i, j;
        while(scanf("%d %d", &n, &m)!=EOF)
        {
            init();//进行初始化
            int u, v;
            for(i=1; i<=m; i++){
                scanf("%d %d", &u, &v);
                addedge(u, v);
            }//建立单向边
    
            for(i=1; i<=n; i++){
                if(!vis[i]){
                    dfs(i);
                }
            }//不知道为什么在此处计数一下连通分量的数目 如果大于1 直接输出0 这样提交 会WA。不写就对
    
            for(i=1; i<=n; i++){
                for(j=head[i]; j!=-1; j=edge[j].next){
                    int v=edge[j].to;
                    if(num[i]!=num[v])//如果连通分量的编号不等
                        du[num[i]]++;//出度++
                }
            }
            int pos; int dd=0;
            for(i=1; i<=cut; i++){
                if(du[i]==0){
                    dd++; pos=i;
                }
            }
            //printf("pos = %d
    ", pos);
            if(dd==1){
            int ans=0;
            for(i=1; i<=n; i++){
                if(num[i]==pos)
                    ans++;
            }
            printf("%d
    ", ans );
            }
            else printf("0
    ");
        }
        return 0;
    }
    

    使用STL-stack模拟的,注意在栈弹出节点的地方的写法,在循环跳出之后再弹出一次,因为遇到u==v的时候就跳出了,而u没有被弹出。

    上面的数组模拟的stack操作也可以再修改一下。

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stack>
    #include <algorithm>
    #define N 10000+10
    #define M 51000+10
    
    using namespace std;
    int n, m;
    struct node
    {
        int to;
        int next;
    }edge[M];//保证存下所有的边
    
    int head[N], dfn[N], low[N], vis[N];
    int num[N], du[N];
    int counter, top, cut, cnt;
    /*
      dfn[]:记录搜索时各顶点的访问次序,即深度优先数
      low[u]:每个节点定义一个low值 low[u]是从u或u的子孙出发通过回边可以到达
             的最低深度优先数
      注意:dfn[]在搜索前进时进行统计 low[]值是在回退的时候进行计算
      du[]:表示出度
    */
    stack<int>q;
    void init()
    {
        memset(head, -1, sizeof(head));
        memset(vis, 0, sizeof(vis));
        memset(dfn, 0, sizeof(dfn));
        memset(low, 0, sizeof(low));
        memset(num, 0, sizeof(num));
        memset(du, 0, sizeof(du));
        counter=1;//begin 1
        cnt=0;//边 计数
        top=0;// stack top
        cut=0;
    }
    
    void addedge(int u, int v)
    {
        edge[cnt].to=v;
        edge[cnt].next=head[u];
        head[u]=cnt++;
    }//静态数组 倒着建边
    
    void dfs(int u)
    {
        dfn[u]=low[u]=counter++;//counter的值从1开始
        vis[u]=1;//标记访问
        q.push(u);
        //遍历从u节点发出的边
        for(int i=head[u]; i!=-1; i=edge[i].next){
            int v=edge[i].to;
            if(!vis[v]){//这是一条树边
                dfs(v);
                low[u]=min(low[u], low[v]);//回溯的时候比较
            }
            else if(vis[v]){
                low[u]=min(low[u], dfn[v]);
            }
        }
        if(low[u]==dfn[u]){
            cut++;//表示已经找到了一个强连通分量
            while(!q.empty() && q.top()!=u){
                vis[q.top()]=2;//
                num[q.top()]=cut;//确定当前这个节点属于哪个强连通分量
                q.pop();
            }
            vis[q.top()]=2;//
            num[q.top()]=cut;//确定当前这个节点属于哪个强连通分量
            q.pop(); //还要将u节点弹出
        }
    }
    
    int main()
    {
        int i, j;
        while(scanf("%d %d", &n, &m)!=EOF)
        {
            init();//进行初始化
            int u, v;
            for(i=1; i<=m; i++){
                scanf("%d %d", &u, &v);
                addedge(u, v);
            }//建立单向边
    
            int cur=0;
            for(i=1; i<=n; i++){
                if(!vis[i]){
                    dfs(i); cur++;
                }
            }
    
            for(i=1; i<=n; i++){
                for(j=head[i]; j!=-1; j=edge[j].next){
                    int v=edge[j].to;
                    if(num[i]!=num[v])//如果连通分量的编号不等
                        du[num[i]]++;//出度++
                }
            }
            int pos; int dd=0;
            for(i=1; i<=cut; i++){
                if(du[i]==0){
                    dd++; pos=i;
                }
            }
            if(dd==1){
            int ans=0;
            for(i=1; i<=n; i++){
                if(num[i]==pos)
                    ans++;
            }
            printf("%d
    ", ans );
            }
            else printf("0
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    《Head First》 MVC运用的设计模式
    unity工具 Animator的使用
    服务器搭建 如果搭建KBE开源服务器
    unity 实战图片挖洞Mask(转载)
    unity博客 推荐(不断补充)
    unity实战 UGUI英雄联盟英雄头顶分段式血条
    unity组成 ToLua
    unity实战 UGUI Text 间距和ContentSizeFitter组件的适配
    unity工具 推荐(不断补充)
    各种单例模式的对比分析
  • 原文地址:https://www.cnblogs.com/yspworld/p/4768709.html
Copyright © 2020-2023  润新知