hdu 3639 Hawk-and-Chicken【强联通Tarjan+Dfs】

来源:互联网 发布:怎么联系淘宝官方客服 编辑:程序博客网 时间:2024/06/05 18:48

Hawk-and-Chicken

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


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
 

Author
Dragon

题目大意;:给你t组数据,每组数据有n个点,m条有向边,表示小朋友a会把手绢丢给b,问谁可能获得最多的手绢,我们设定初始的时候每个人都有一个手绢,但是自己投自己的手绢不作数。一共输出两个:

1、输出最大手绢数,

2、输出可能获得最大手绢数的人编号。


思路:


1、因为有向图中可能有环存在,我们先用Tarjan算法染色缩点,得到一个DAG图。


2、在DAG图中反向建边,然后找入度为0的点Dfs求累加能到达的点的手绢和,不难理解,如果有一个点u想把手绢丢给v,那么在反图中,从v一定能够找到u,因为在正向图中找到所有的u比较麻烦,所以我们建反图从v来找u。

然后维护累加和的最大值,然后对于每一个能够可能获得这个维护之后的最大值的节点,都放在一个数组里边,然后排序输出即可。


注意的点:


1、会有重边。

2、考虑要周全,千万要将该初始化的数组都初始化了,要不然容易WA

AC代码:

#include<stdio.h>#include<string.h>#include<vector>#include<iostream>#include<algorithm>using namespace std;#define maxn 5250int kase;bool map[5250][5250];vector<int >mp[maxn];vector<int >mp2[maxn];int stack[maxn*1000];int sum[maxn];int degree[maxn];int vis[maxn];int low[maxn];int dfn[maxn];int color[maxn];int dp[maxn];int ans[maxn];int n,m,cnt,tt,sig,leijia;void Tarjan(int u){    vis[u]=1;    low[u]=dfn[u]=cnt++;    stack[++tt]=u;    for(int i=0;i<mp[u].size();i++)    {        int v=mp[u][i];        if(vis[v]==0)Tarjan(v);        if(vis[v]==1)low[u]=min(low[u],low[v]);    }    if(low[u]==dfn[u])    {        sig++;        do        {            vis[stack[tt]]=-1;            color[stack[tt]]=sig;        }        while(stack[tt--]!=u);    }}void Dfs(int u){    vis[u]=1;    leijia+=sum[u];//对于每个入度为0的点,能够反向建图到达的点统计起来。    for(int i=0;i<mp2[u].size();i++)    {        int v=mp2[u][i];        if(vis[v]==0)Dfs(v);    }}void Slove(){    tt=-1;sig=0;cnt=1;    for(int i=1;i<=n;i++)    {        if(vis[i]==0)Tarjan(i);    }//Tarjan染色    for(int i=1;i<=n;i++)    {        for(int j=0;j<mp[i].size();j++)        {            int v=mp[i][j];            if(color[i]!=color[v])            {                if(map[color[v]][color[i]]==true)continue;                mp2[color[v]].push_back(color[i]);                degree[color[i]]++;                map[color[v]][color[i]]=true;            }        }    }//缩点重新建边,并且去重边。这里注意一下判重数组map【】【】我开的类型是bool的,开int会超内存,很蛋疼    memset(vis,0,sizeof(vis));    for(int i=1;i<=n;i++)sum[color[i]]++;//对于每一个强联通分量统计其节点数。    for(int i=1;i<=sig;i++)dp[i]=sum[i];//数组名字不重要.......一开始想用dp搞,所以数组就开了这么个名字,我的解法和dp没有半毛钱关系    for(int i=1;i<=sig;i++)    {        if(degree[i]==0)//对于缩点之后的点,如果入度为0,进入Dfs        {            memset(vis,0,sizeof(vis));            leijia=0;            Dfs(i);            dp[i]=max(dp[i],leijia);        }    }    int maxnn=0;    for(int i=1;i<=sig;i++)    {        maxnn=max(dp[i],maxnn);    }//得到这个最大值    int ttt=0;    for(int i=1;i<=sig;i++)    {        if(dp[i]==maxnn)        {            for(int j=1;j<=n;j++)            {                if(color[j]==i)                {                    ans[ttt++]=j;//并且将可能的节点都放进答案数组中                }            }        }    }    sort(ans,ans+ttt);    printf("Case %d: %d\n",++kase,maxnn-1);//去掉自己那个手绢。    for(int i=0;i<ttt;i++)    {        if(i==0)printf("%d",ans[i]-1);//建图习惯,大家忽视掉就好        else printf(" %d",ans[i]-1);    }    printf("\n");}void init(){    memset(degree,0,sizeof(degree));    memset(map,false,sizeof(map));    memset(sum,0,sizeof(sum));    memset(dp,0,sizeof(dp));    memset(vis,0,sizeof(vis));    memset(low,0,sizeof(low));    memset(dfn,0,sizeof(dfn));    memset(color,0,sizeof(color));    memset(dp,0,sizeof(dp));}int main(){    int t;    kase=0;    scanf("%d",&t);    while(t--)    {        scanf("%d%d",&n,&m);        init();        for(int i=1;i<=n;i++)mp[i].clear(),mp2[i].clear();        for(int i=0;i<m;i++)        {            int x,y;            scanf("%d%d",&x,&y);            x++;y++;            mp[x].push_back(y);        }        Slove();    }}/*43 43 00 11 22 04 31 02 03 04 33 22 02 16 71 54 11 12 42 00 24 5*/












0 0
原创粉丝点击