zoukankan      html  css  js  c++  java
  • POJ2186(有向图缩点)

    Popular Cows
    Time Limit: 2000MS   Memory Limit: 65536K
    Total Submissions: 28379   Accepted: 11488

    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
    题意:A认为B受欢迎,B认为C受欢迎,那么A就认为C受欢迎。给定N头牛,M个认为受欢迎情况。确定受所有牛欢迎的这些牛的数目。
    思路:首先判断有向图是否连通,若不连通则答案为0。若连通则进行缩点(注意有向图与无向图的缩点的不同之处),缩点之后得到一个有向树。若树中存在多个叶子结点,则答案为0,否则答案为 缩成叶子结点的那个连通分量重结点的数目。
    下面是第二种有向图缩点方法,一次dfs,边反向后再进行一次rdfs.解释见代码
    #include"cstdio"
    #include"cstring"
    #include"vector"
    using namespace std;
    const int MAXN=10005;
    int V,E;
    vector<int> G[MAXN];
    vector<int> rG[MAXN];
    vector<int> vs;
    bool used[MAXN];
    int cmp[MAXN];
    void add_edge(int u,int v)
    {
        G[u].push_back(v);
        rG[v].push_back(u);
    }
    void dfs(int u)
    {
        used[u]=true;
        for(int i=0;i<G[u].size();i++)
            if(!used[G[u][i]])    dfs(G[u][i]);//假设u所能访问的结点都处于同一连通分量 
        vs.push_back(u); //后续遍历 
    }
    void rdfs(int u,int k)
    {
        used[u]=true;
        cmp[u]=k;
        for(int i=0;i<rG[u].size();i++)
            if(!used[rG[u][i]])    rdfs(rG[u][i],k);
    }
    int scc()
    {
        memset(used,false,sizeof(used));
        for(int i=1;i<=V;i++)
            if(!used[i])    dfs(i);
        memset(used,false,sizeof(used));
        int k=0;
        for(int i=vs.size()-1;i>=0;i--)//倒着访问,保证边反向后各个连通分量互不影响 
            if(!used[vs[i]])    rdfs(vs[i],k++);//对应后续遍历,若边反向后,起点u仍能遍历整个连通分量,那么它们处以同一连通分量中.否则处于不同的连通分量 
        return k;
    }
    int deg[MAXN];
    int seek()
    {
        memset(deg,0,sizeof(deg));
        int k=scc();
        for(int i=1;i<=V;i++)
            for(int j=0;j<G[i].size();j++)
            {
                int to=G[i][j];
                if(cmp[i]!=cmp[to])
                {
                    deg[cmp[i]]++;
                }
            }
        
        int v=-1;
        int flag=0;
        int ans=0;
        for(int i=1;i<=V;i++)
            if(deg[cmp[i]]==0)
            {
                ans++;
                if(cmp[i]!=v)
                {
                    v=cmp[i];
                    flag++;
                }
                if(flag>1)    return 0;
            }
        return ans;
    }
    int main()
    {
        while(scanf("%d%d",&V,&E)!=EOF)
        {
            for(int i=1;i<=V;i++)
            {
                vs.clear();
                G[i].clear();
                rG[i].clear();
                cmp[i]=0;
            }
            for(int i=0;i<E;i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                add_edge(u,v);
            }
            int ans=seek();
            printf("%d
    ",ans);        
        }
        
        return 0;
    }
    标准tarjan算法对有向图缩点。
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int MAXN=10005;
    vector<int> mp[MAXN];
    int n,m;
    int dfn[MAXN],low[MAXN],time;
    int stack[MAXN],top;
    bool ins[MAXN];
    int belong[MAXN],cnt;
    void dfs(int u)
    {
        dfn[u]=low[u]=++time;
        stack[top++]=u;
        ins[u]=true;
        for(int i=0;i<mp[u].size();i++)
        {
            int v=mp[u][i];
            if(!dfn[v])
            {
                dfs(v);
                low[u]=min(low[u],low[v]);
            }
            else if(ins[v])    low[u]=min(low[u],dfn[v]);
        }
        if(dfn[u]==low[u])
        {
            cnt++;
            int v;
            do{
                v=stack[--top];
                belong[v]=cnt;
                ins[v]=false;
            }while(u!=v);
        }
    }
    int deg[MAXN];
    void cal()
    {
        memset(deg,0,sizeof(deg));
        int res=0;
        for(int i=1;i<=n;i++)
            for(int j=0;j<mp[i].size();j++)
            {
                int v=mp[i][j];
                if(belong[i]!=belong[v])
                {
                    deg[belong[i]]++;    
                }
            }
        int mark;
        for(int i=1;i<=cnt;i++)
            if(deg[i]==0)
            {
                mark=i;
                res++;
            }
        if(res!=1)
        {
            printf("0
    ");
            return;
        }
        res=0;
        for(int i=1;i<=n;i++)
            if(belong[i]==mark)
                res++;
        printf("%d
    ",res);
    }
    int main()
    {
        
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            for(int i=1;i<=n;i++)
                mp[i].clear();
            memset(dfn,0,sizeof(dfn));
            memset(low,0,sizeof(low));
            time=0;
            top=0;
            cnt=0;
            memset(ins,false,sizeof(ins));
            for(int i=0;i<m;i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                mp[u].push_back(v);
            }
            
            for(int i=1;i<=n;i++)
                if(!dfn[i])
                    dfs(i);
            cal();
        }    
        return 0;
    }


  • 相关阅读:
    Bootstrap 网页乱码
    西游记人物
    球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次落地时,共经过多少米?第10次反弹多高?
    利用条件运算符的嵌套来完成此题:学习成绩> =90分的同学用A表示,60-89分之间的用B表示,60分以下的用C表示。
    s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加由用户控制。
    实现判断字符串的开头和结尾
    值类型和引用类型
    随机生成4位验证码
    实现 从1-36中随机产生6个不重复的中奖号码
    冒泡排序
  • 原文地址:https://www.cnblogs.com/program-ccc/p/5167845.html
Copyright © 2011-2022 走看看