HDU 3639 Hawk-and-Chicken(强连通分量+缩点)

来源:互联网 发布:在哪买淘宝号 编辑:程序博客网 时间:2024/06/13 01:33


Hawk-and-Chicken

Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 45 Accepted Submission(s): 28 
Problem Description
Kids in kindergarten enjoy playing a game called Hawk-and-Chicken. But there always exists a big problem: every kid in this game want to play the role of Hawk.  
So the teacher came up with an idea: Vote. Every child have some nice handkerchiefs, and if he/she think someone is suitable for the role of Hawk, he/she gives a handkerchief to this kid, which means this kid who is given the handkerchief win the support. Note the support can be transmitted. Kids who get the most supports win in the vote and able to play the role of Hawk.(A note:if A can win
support from B(A != B) A can win only one support from B in any case the number of the supports transmitted from B to A are many. And A can't win the support from himself in any case.
If two or more kids own the same number of support from others, we treat all of them as winner.
Here's a sample: 3 kids A, B and C, A gives a handkerchief to B, B gives a handkerchief to C, so C wins 2 supports and he is choosen to be the Hawk.
 
Input
There are several test cases. First is a integer T(T <= 50), means the number of test cases.
Each test case start with two integer n, m in a line (2 <= n <= 5000, 0 <m <= 30000). n means there are n children(numbered from 0 to n - 1). Each of the following m lines contains two integers A and B(A != B) denoting that the child numbered A give a handkerchief to B.
 
Output
For each test case, the output should first contain one line with "Case x:", here x means the case number start from 1. Followed by one number which is the total supports the winner(s) get. 
Then follow a line contain all the Hawks' number. The numbers must be listed in increasing order and separated by single spaces. 
 
Sample Input
24 33 22 02 13 31 02 10 2
 
Sample Output
Case 1: 20 1Case 2: 20 1 2
 
Author
Dragon
 


题意:

        给你一个有向图,如果从u点能到达v点,那么说u是v的粉丝,现在要你按序输出那些粉丝数目最多的点编号.

分析:

        假设该图是一个强连通图,那么任一点都有n-1个粉丝(即n-1个点能到达它).所以我们把该图缩点变成一个新的DAG图.

把强连通子图,缩成一个点,然后进行接下来的判断。

然后我们可以采取反向建图的策略,这样支持数最大的节点只可能在入度为0的点中产生,只需要对所有入度为0的点dfs一遍计算它能到达的所有顶点数即可。但是还有一个问题,当遇到有向环时(也是一个强连通分量),dfs的过程中对于环中的最后一个点会产生错误的判断,显然这个环中的所有点状态完全一样,可以使用Tarjan的强连通分量进行缩点。

#include <iostream>#include <algorithm>#include <string.h>#include <stdio.h>#include <cmath>#include <vector>#include <queue>#include <stack>#include <utility>using namespace std;const int INF = 0x3fffffff;const int maxn = 5005;int n, m;int dfs_clock, scc_cnt;int pre[maxn], sccno[maxn], low[maxn];int num[maxn];//每个连通分量有多少点;int indegree[maxn];//新dag逆图的入度int fans[maxn];//每个点的粉丝数量int visited[maxn];vector<int> g1[maxn], g2[maxn];stack<int> sta;void tarjan(int u) {    pre[u] = low[u] = ++dfs_clock;    sta.push(u);    for (int i = 0; i < g1[u].size(); ++i) {        int v = g1[u][i];        if (!pre[v]) {            tarjan(v);            low[u] = min(low[u], low[v]);        } else if (!sccno[v]){            low[u] = min(low[u], pre[v]);        }    }        if (low[u] == pre[u]) {        scc_cnt++;        num[scc_cnt] = 0;        while (1) {            int x = sta.top();            sccno[x] = scc_cnt;            num[scc_cnt]++;            sta.pop();            if (x == u)                break;        }    }}void find() {    dfs_clock = scc_cnt = 0;    memset(pre, 0, sizeof(pre));    memset(sccno, 0, sizeof(sccno));    for (int i = 0; i < n; ++i) {        if (!pre[i])            tarjan(i);    }}int dfs(int u) {    int sum = 0;    visited[u] = 1;    for (int i = 0; i < g2[u].size(); ++i) {        int v = g2[u][i];        if (!visited[v]) {            sum += num[v] + dfs(v);        }    }        //cout << sum << endl;    return sum;}int main() {        //freopen("in.txt", "r", stdin);    int t;    scanf("%d", &t);    for (int shit = 1; shit <= t; ++shit) {        scanf("%d%d", &n, &m);        for (int i = 0; i < n; ++i) {            g1[i].clear();            g2[i].clear();        }                for (int i = 0; i < m; ++i) {            int a, b;            scanf("%d%d", &a, &b);            g1[a].push_back(b);        }        ::find();        //逆置;        memset(indegree, 0, sizeof(indegree));        for (int i = 0; i < n; ++i) {            for (int j = 0; j < g1[i].size(); ++j) {                int u = g1[i][j];                int v = i;                    //cout << u << "    " << v << endl;                //在一个强连通分量,缩点---》把一个强连通分量看成一个点                if (sccno[u] != sccno[v]) {                    g2[sccno[u]].push_back(sccno[v]);                    indegree[sccno[v]]++;                }            }        }        int fmax = -1;        memset(fans, 0, sizeof(fans));        for (int i = 1; i <= scc_cnt; ++i) {            if (!indegree[i]) {                memset(visited, 0, sizeof(visited));                    //    在连通分量中的粉丝不包括自己                fans[i] = num[i] - 1 + dfs(i);                fmax = max(fans[i], fmax);            }        }        bool win[maxn];        for (int i = 0; i < n; ++i) {            win[i] = false;            if (fans[sccno[i]] == fmax) {                win[i] = true;            }        }        int first = 0;        printf("Case %d: %d\n", shit, fmax);        for (int i = 0; i < n; ++i) {            if (win[i]) {                if (!first) {                    printf("%d",i);                    first = 1;                } else {                     printf(" %d",i);                }            }        }        printf("\n");    }}