POJ2186Popular Cows(强连通分量+缩点)

来源:互联网 发布:算法的复杂性 编辑:程序博客网 时间:2024/04/16 12:29

                                                                                                                       Popular Cows
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 24614 Accepted: 10096

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 31 22 12 3

Sample Output

1

Hint

Cow 3 is the only cow of high popularity.


题目来源:http://poj.org/problem?id=2186

    

题目定义奶牛之间的崇拜为有向关系,并且假设A崇拜B,B崇拜C,则A也崇拜C,现在题目想要知道有多少头奶牛被除自身外的其他所有奶牛崇拜。用极大强连通分量+缩点。


构造模型:

N个顶点的有向图G,有M条边。求一共有多少个点,满足这样的条件:所有其它的点都可以到达这个点。

 

首先,这个题的N和M都非常大,暴搜肯定TLE。

考虑一下,如果图G是一棵有向树,那么问题就变的很简单了,因为当且仅当这棵树只有一个叶子结点(出度为0的点)时,树上的其他所有结点都能到达这个点。而当有向树上有1个以上的叶子时,都是无解的。

由于树是无环的,下面成这样的一棵有向树为 有向无环树DAG

 

那么我们能否把图转化为树去求解呢?

 

首先可以想到的是,如果图G中包含有环,那么就可以把这个环缩成一个点,因为环中的任意两个点可以到达,环中所有的点具有相同的性质,即它们分别能到达的点集都是相同的,能够到达它们的点集也是相同的。

 

那么是否只有环中的点才具有相同的性质呢?进一步的考虑,图中的每一个极大强连通分量中的点都具有相同的性质。所以,如果把图中的所有极大强连通分量求出后,对每个极大强连通分量缩点,就可以把图收缩成一棵有向无环树DAG,那么只要判断出度为0的缩点是否只有1个,若DAG中有且仅有1个这样的缩点,则输出缩点(图G的极大强连通分量)内所包含的图G的结点个数,问题就解决。


<span style="font-size:18px;">#include <cstdio>#include <iostream>#include <cstring>#include <cmath>#include <string>#include <algorithm>#include <queue>#include <stack>using namespace std;const double PI = acos(-1.0);const double e = 2.718281828459;const double eps = 1e-8;const int MAXN = 10010;const int MAXM = 5*MAXN;struct Edge{    int v;    int next;} edge[MAXM]; //边的集合struct shrink_point{    int in;    int out;    int num;} sp[MAXN];stack<int>Q;int head[MAXN];//顶点集合int instack[MAXN];//标记是否在stack中int scc[MAXN];//各顶点属于哪个强连通分量int DFN[MAXN];//节点u搜索的序号(时间戳)int LOW[MAXN];//u或u的子树能够追溯到的最早的栈中节点的序号(时间戳)int n, m;//n:点的个数;m:边的条数int edge_cnt;//边的计数器int Index;//序号(时间戳)int scc_cnt;//有多少个强连通分量void add_edge(int u, int v)//邻接表存储{    edge[edge_cnt].v = v;    edge[edge_cnt].next = head[u];    head[u] = edge_cnt++;}void Tarjan(int u){    DFN[u] = LOW[u] = ++Index;    instack[u] = 1;    Q.push(u);    for(int i = head[u]; i != -1; i = edge[i].next)    {        int v = edge[i].v;        if (!DFN[v])//如果点v没被访问        {            Tarjan(v);            if(LOW[v] < LOW[u])                LOW[u] = LOW[v];        }        //如果点v已经被访问过,后向边        else if(instack[v] && DFN[v]<LOW[u])            LOW[u] = DFN[v];    }    if(DFN[u] == LOW[u])    {        scc_cnt++;        int j;        do        {            j = Q.top();            Q.pop();            instack[j] = 0;            scc[j] = scc_cnt;            sp[scc_cnt].num++;        }        while(j != u);    }}int solve(){    scc_cnt = Index = 0;    memset(DFN, 0, sizeof(DFN));    memset(LOW, 0, sizeof(LOW));    memset(sp, 0, sizeof(shrink_point)*MAXN);    while(!Q.empty())        Q.pop();    for(int i = 1; i <= n; i++)        if (!DFN[i])            Tarjan(i);    for(int i = 1; i <= n; i++)    {        for(int k = head[i]; k != -1; k = edge[k].next)        {            int j = edge[k].v;            if(scc[i] != scc[j])            {                sp[scc[i]].out++;                sp[scc[j]].in++;                //printf("out-%d    in-%d\n", scc[i], scc[j]);            }        }    }    //printf("%d\n", scc_cnt);    int cnt = 0;    int id = 0;    for(int i = 1; i <= scc_cnt; i++)        if(sp[i].out == 0)        {            cnt++;            id = i;        }    if(cnt == 1)        return sp[id].num;    return 0;}int main(){    //freopen("in.txt", "r", stdin);    //freopen("out.txt", "w", stdout);    int u, v;    while(cin>>n>>m)    {        edge_cnt = 0;        memset(head, -1, sizeof(head));        for(int i = 1; i <= m; i++)        {            scanf("%d %d", &u, &v);            add_edge(u, v);        }        int ans = solve();        printf("%d\n", ans);    }    return 0;}</span>


参考: http://blog.csdn.net/justlovetao/article/details/6673602

参考: http://blog.csdn.net/lyy289065406/article/details/6764104


0 0
原创粉丝点击