hdoj 3639 Hawk-and-Chicken 【有向图SCC】【缩点后反向建图 求最大传递值】

本文深入探讨了游戏开发领域的核心技术,包括游戏引擎、动画、视觉特效等,旨在为游戏开发者提供实用的技术指导。

Hawk-and-Chicken

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2353    Accepted Submission(s): 698


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 totalsupports 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
2 4 3 3 2 2 0 2 1 3 3 1 0 2 1 0 2
 

Sample Output
Case 1: 2 0 1 Case 2: 2 0 1 2
 

题意:有N个孩子编号从0到N-1,在玩 ”老鹰捉小鸡“ 游戏。每局游戏只有一个人当老鹰,为了公平,老师采用了传递手帕的方式来确定谁来当老鹰。
规则如下
1,自己不能给自己传递手帕;
2,当A传给B,B又传给C后,可以认为A传给了C手帕;
3,不管有多少个人传给B手帕,当B传递给A时,只认为A被传递了一张手帕;
4,最后被传递手帕最多的人是赢家;
现在已经给出M组传递关系<a,b>,表示a会传递给b一张手帕。
问你  最后的赢家被传递了多少张手帕 ,若有多个赢家,按编号升序输出。

第一次分析:明显和有向图SCC有关的题目,一开始我想的是用Floyd预处理传递闭包,然后SCC缩点统计每个SCC入度,最后枚举所有SCC,选取最大的scc[ i ].size() - 1 + in[ i ]值(1 <= i <= scc_cnt)。最后发现N值过大,没法预处理传递闭包,无语死了,还奉献一次MLE。

第二次分析:对一个编号为u的SCC而言,预处理传递闭包 就是为了 求出 有多少个SCC可以通过边的关系传递到u,反过来说,就是求 u 通过边的关系可以传递到 多少个SCC。这样思路就清晰了。

思路:对原有向图求出SCC,缩点后反向建图。对新图,选取入度为0的SCC,进行一次DFS,统计出该SCC的传递值,一直更新这个值即可。输出部分,枚举所有点,比较这个点所在SCC的传递值 和 上一步求出的答案,相同则输出。


AC代码:注意在DFS求SCC传递值的时候,必须用一个数组来标记一个点是否查询。

给出一组数据

10
3 3
1 0
2 0
1 2
若没有标记会输出 3, 有标记才输出正确答案2.。当然这是我个人的理解。


#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>
#define MAXN 5000+10
#define MAXM 30000+100
using namespace std;
struct Edge
{
    int from, to, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int low[MAXN], dfn[MAXN];
int dfs_clock;
int sccno[MAXN], scc_cnt;
vector<int> scc[MAXN];
stack<int> S;
bool Instack[MAXN];
int N, M;
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v)
{
    Edge E = {u, v, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
void getMap()
{
    int a, b;
    while(M--)
    {
        scanf("%d%d", &a, &b);
        addEdge(a, b);
    }
}
void tarjan(int u, int fa)
{
    int v;
    low[u] = dfn[u] = ++dfs_clock;
    S.push(u);  Instack[u] = true;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        if(!dfn[v])
        {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
        }
        else if(Instack[v])
            low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        scc_cnt++;
        scc[scc_cnt].clear();
        for(;;)
        {
            v = S.top(); S.pop();
            Instack[v] = false;
            sccno[v] = scc_cnt;
            scc[scc_cnt].push_back(v);
            if(v == u) break;
        }
    }
}
void find_cut(int l, int r)
{
    dfs_clock = scc_cnt = 0;
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(sccno, 0, sizeof(sccno));
    memset(Instack, false, sizeof(Instack));
    for(int i = l; i <= r; i++)
        if(!dfn[i]) tarjan(i, -1);
}
int num[MAXN];//记录SCC里面任意一点 获得的手帕数目
int in[MAXN];//记录SCC的入度
vector<int> G[MAXN];//存储缩点后新图
void suodian()//缩点
{
    for(int i = 1; i <= scc_cnt; i++) G[i].clear(), in[i] = 0;
    for(int i = 0; i < edgenum; i++)
    {
        int u = sccno[edge[i].from];
        int v = sccno[edge[i].to];
        if(u != v)//反向建图
            G[v].push_back(u), in[u]++;
    }
}
bool vis[MAXN];//标记该点是否查询过 这个数组一定要用
int sum;
void DFS(int u)
{
    sum += scc[u].size();//WA到死!!! 这里 加错了。。。
    vis[u] = true;
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if(vis[v]) continue;
        DFS(v);
    }
}
int k = 1;
void solve()
{
    int ans = 0;
    for(int i = 1; i <= scc_cnt; i++)
    {
        num[i] = 0;//初始化
        if(!in[i])//只有入度为0的SCC里面的点 才有可能得到最多的传递值
        {
            sum = 0;//DFS过程中把自己也算上了
            memset(vis, false, sizeof(vis));
            DFS(i);
            num[i] = sum - 1;//SCC里面自己不能传递给自己
            ans = max(num[i], ans);
        }
    }
    printf("Case %d: %d\n", k++, ans);
    int p = 0;
    for(int i = 0; i <= N-1; i++)
    {
        if(num[sccno[i]] == ans)
        {
            if(p > 0) printf(" ");
            printf("%d", i);
            p++;
        }
    }
    printf("\n");
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &N, &M);
        init();
        getMap();
        find_cut(0, N-1);
        suodian();
        solve();
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值