poj 2186 Popular Cows

来源:互联网 发布:进销存带人力资源软件 编辑:程序博客网 时间:2024/05/16 04:41

原题:

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 29811 Accepted: 12097
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.
Source

USACO 2003 Fall
中文大意:
有一群牛,这群牛当中有些牛认为别的牛是大明星,如果A认为B是明星而且B认为C是明星,那么A认为C也是明星。现在问你被所有牛都认为是明星的牛有多少。

//#include<bits/stdc++.h>#include<iostream>#include<vector>#include<cstring>#include<map>using namespace std;int V,E,NE;vector<int> G[10001];vector<int> rG[10001];vector<int> vs;bool used[10001];int cmp[10001];void add_edge(int from,int to){    G[from].push_back(to);    rG[to].push_back(from);}void dfs(int v){    used[v]=true;    for(int i=0;i<G[v].size();i++)    {        if(!used[G[v][i]])            dfs(G[v][i]);    }    vs.push_back(v);}void rdfs(int v,int k){    used[v]=true;    cmp[v]=k;    for(int i=0;i<rG[v].size();i++)    {        if(!used[rG[v][i]])            rdfs(rG[v][i],k);    }}int scc(){    memset(used,0,sizeof(used));    vs.clear();    for(int v=0;v<V;v++)        if(!used[v])            dfs(v);    memset(used,0,sizeof(used));    int k=0;    for(int i=vs.size()-1;i>=0;i--)        if(!used[vs[i]])            rdfs(vs[i],k++);    return k;}void ini(int n){    for(int i=0;i<=n;i++)    {        G[i].clear();        rG[i].clear();    }}//fstream in,out;int main(){    ios::sync_with_stdio(false);//  in.open("data.txt");//  out.open("input.txt");    while(cin>>V>>E)    {        ini(V);        for(int i=1;i<=E;i++)        {            int a,b;            cin>>a>>b;            add_edge(a-1,b-1);        }        int con=scc();        int num=0,u=0;        for(int i=0;i<V;i++)        {            if(cmp[i]==con-1)            {                num++;                u=i;            }        }        memset(used,false,sizeof(used));        rdfs(u,0);        for(int i=0;i<V;i++)        {            if(!used[i])            {                num=0;break;            }        }        cout<<num<<endl;    }//  in.close();//  out.close();    return 0;}

解答:
这是我做的第一个强连通分量的题目,都让我想蒙了。主要思路就是算出强连通分量,标记所有缩点。然后找到最终所有点都指向的那个“根节点”。第一次的想法是考虑用并查集来寻找根,结果没成功= =,后来考虑用最近公共祖先LCA,貌似想法是对的,但是代码没写出来=_= 。
第二次发现找最后的那个“根”节点可以用拓扑排序每次来进行寻找,这也是正确的想法,如图(例子),其中每个圈代表强连通分量形成的缩点:
这里写图片描述
最后算出来的结果应该是又两头牛,即7和8因为所有最后的传递都指向78这个缩点。按照拓扑排序的方法寻找最后“根”,每次找到入度为0的缩点,然后删除。最后剩下的那个缩点就是答案,如果剩下的不止一个,那么没有答案,输出0。
例如,先删除9,10
这里写图片描述
删除1,2
这里写图片描述
删除3,4,5
这里写图片描述
删除6后剩下7,8就是答案
这里写图片描述
这里我是先算出所有强连通分量的缩点,然后对缩点重新编号。然后用拓扑排序,最后代码写了好长不说,结果还超时了。可能是我用的拓扑排序的时间复杂度比较高,不过可以用前向星加优先队列优化成加法的时间复杂度。但是感觉代码更不好写了。最后看了看挑战程序设计竞赛那本书,知道了用代码所示的那个方法找到的强连通分量的同时就已经进行过拓扑排序,那么最后找的那个缩点就是答案,再对最后找到的那个缩点判断是否能够到达全部的点就可以了。
图论的代码能有好几个月没写过了,有点蒙。=_=~

0 0
原创粉丝点击