【HDU】3639Hawk-and-Chicken(强联通缩点+DFS)

本文介绍了一种解决幼儿园孩子玩老鹰抓小鸡游戏时如何公平选择老鹰角色的投票算法。该算法利用了图论中的Tarjan缩点算法和连通分量的概念,通过构建投票关系图,确定获得最多票数的孩子。文章详细解释了解题思路,包括如何处理投票的传递性和统计最终得票数。
Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4207    Accepted Submission(s): 1316


 

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

 

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

 

 

Author

Dragon

 

 

Source

2010 ACM-ICPC Multi-University Training Contest(19)——Host by HDU

 

 

Recommend

lcy

思路转载自:https://www.cnblogs.com/00isok/p/10049925.html

题目大意:有一群孩子正在玩老鹰抓小鸡,由于想当老鹰的人不少,孩子们通过投票的方式产生,但是投票有这么一条规则:投票具有传递性,A支持B,B支持C,那么C获得2票(A.B共两票),输出最多能获得的票数是多少张和获得最多票数的人是谁?(如果有多个人获得的票数都是最多的,就将他们全部输出)。

解题分析:不难看出,同一连通分量的所有点得到的票数肯定是相同的,所以我们先将原图进行Tarjan缩点。对于缩完点后的图,我们发现,票数最多的人一定在出度为0的"点"中,因为如果票数最多的点不是出度为0的"点",那么它所到达的那个"点"的票数一定大于当前"点"的票数,这与当前"点"票数最多相矛盾。然后考虑对入度为0的"点"的票数统计,我们可以在缩点后进行重新构图,相互连通的"点"之间建立反边,这样方便DFS统计当前"点"的票数。需要注意的是,当前连通分量中所有的点在当前连通分量中获得的票数为num[now]-1(因为要除去自己),然后再加上以这个点为起点,能够到达所有连通分量的点数,即为这个点能够得到的票数。

代码:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

#define clr(a,b) memset(a,b,sizeof(a))
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define pb push_back
#define mp make_pair
const int N = 5e3+10, M = 3e4+10;
int n,m,tot,cnt,cnt1,scc,top,sum;
int head[N],head1[N],low[N],dfn[N],belong[N],stk[N],instk[N],num[N],outdeg[N],vis[N],ans[N];
vector<int>G[N];
struct Edge{
    int to,next;
}edge[M],edge1[M];
void init(){
    rep(i,0,n)G[i].clear();
    tot=cnt=cnt1=scc=top=0;
    clr(head,-1);clr(head1,-1);clr(low,0);clr(num,0);clr(ans,0);
    clr(dfn,0);clr(stk,0);clr(instk,0);clr(belong,0);clr(outdeg,0);
}
void addedge(int u,int v){
    edge[cnt].to=v,edge[cnt].next=head[u];
    head[u]=cnt++;
}
void addedge1(int u,int v){      //添加缩点后的反向边
    edge1[cnt1].to=v,edge1[cnt1].next=head1[u];
    head1[u]=cnt1++;
}
void Tarjan(int u){
    dfn[u]=low[u]=++tot;
    stk[++top]=u;instk[u]=1;    
    for(int i=head[u];~i;i=edge[i].next){
        int v=edge[i].to;
        if(!dfn[v]){
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }else if(instk[v])low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        scc++;
        while(true){
            int v=stk[top--];
            instk[v]=0;
            belong[v]=scc;
            G[scc].pb(v);
            if(v==u)break;
        }
    }
}
void dfs(int u){   //统计以u连通分量为起点能够到达所有的连通分量的点数之和
    vis[u]=1;
    sum+=num[u];
    for(int i=head1[u];~i;i=edge1[i].next){
        int v=edge1[i].to;
        if(!vis[v])dfs(v);
    }
}
int main(){
    int T,ncase=0;scanf("%d",&T);
    while(T--){    
        init();
        scanf("%d%d",&n,&m);
        rep(i,1,m) {
            int u,v;scanf("%d%d",&u,&v);
            u++,v++;
            addedge(u,v);
        }
        rep(i,1,n) if(!dfn[i]){
            Tarjan(i);
        }
        rep(i,1,n) num[belong[i]]++;     //求出每个连通分量的点数
        rep(u,1,n) for(int i=head[u];~i;i=edge[i].next){
            int v=edge[i].to;
            int tmp1=belong[u],tmp2=belong[v];
            if( tmp1!=tmp2 ){        //缩点后,进行重新构图,并且添加的是反向边,方便进行票数的统计
                addedge1(tmp2,tmp1);outdeg[tmp1]++; 
            }
        }
        int mx=-1;
        rep(i,1,scc) if(!outdeg[i]){    //统计所有出度为0的连通分量所能够得到的票数
            clr(vis,0);
            sum=0;dfs(i);
            ans[i]=sum-1;     //获得的票数为路径上所有的点数和-1,因为自己不能获得自己的票
            mx=max(mx,ans[i]);
        }    
        printf("Case %d: %d\n",++ncase,mx);
        bool first=false;
        rep(i,1,n) if(ans[belong[i]]==mx){    //将所有票数等于最大票数的点输出
            if(!first)printf("%d",i-1);
            else printf(" %d",i-1);
            first=true;
        }puts("");
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值