• HDU 3639 Hawk-and-Chicken (强连通缩点+DFS)


    <题目链接>

    题目大意:

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

    解题分析:

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

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <vector>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 #define clr(a,b) memset(a,b,sizeof(a))
     8 #define rep(i,s,t) for(int i=s;i<=t;i++)
     9 #define pb push_back
    10 #define mp make_pair
    11 const int N = 5e3+10, M = 3e4+10;
    12 int n,m,tot,cnt,cnt1,scc,top,sum;
    13 int head[N],head1[N],low[N],dfn[N],belong[N],stk[N],instk[N],num[N],outdeg[N],vis[N],ans[N];
    14 vector<int>G[N];
    15 struct Edge{
    16     int to,next;
    17 }edge[M],edge1[M];
    18 void init(){
    19     rep(i,0,n)G[i].clear();
    20     tot=cnt=cnt1=scc=top=0;
    21     clr(head,-1);clr(head1,-1);clr(low,0);clr(num,0);clr(ans,0);
    22     clr(dfn,0);clr(stk,0);clr(instk,0);clr(belong,0);clr(outdeg,0);
    23 }
    24 void addedge(int u,int v){
    25     edge[cnt].to=v,edge[cnt].next=head[u];
    26     head[u]=cnt++;
    27 }
    28 void addedge1(int u,int v){      //添加缩点后的反向边
    29     edge1[cnt1].to=v,edge1[cnt1].next=head1[u];
    30     head1[u]=cnt1++;
    31 }
    32 void Tarjan(int u){
    33     dfn[u]=low[u]=++tot;
    34     stk[++top]=u;instk[u]=1;    
    35     for(int i=head[u];~i;i=edge[i].next){
    36         int v=edge[i].to;
    37         if(!dfn[v]){
    38             Tarjan(v);
    39             low[u]=min(low[u],low[v]);
    40         }else if(instk[v])low[u]=min(low[u],dfn[v]);
    41     }
    42     if(dfn[u]==low[u]){
    43         scc++;
    44         while(true){
    45             int v=stk[top--];
    46             instk[v]=0;
    47             belong[v]=scc;
    48             G[scc].pb(v);
    49             if(v==u)break;
    50         }
    51     }
    52 }
    53 void dfs(int u){   //统计以u连通分量为起点能够到达所有的连通分量的点数之和
    54     vis[u]=1;
    55     sum+=num[u];
    56     for(int i=head1[u];~i;i=edge1[i].next){
    57         int v=edge1[i].to;
    58         if(!vis[v])dfs(v);
    59     }
    60 }
    61 int main(){
    62     int T,ncase=0;scanf("%d",&T);
    63     while(T--){    
    64         init();
    65         scanf("%d%d",&n,&m);
    66         rep(i,1,m) {
    67             int u,v;scanf("%d%d",&u,&v);
    68             u++,v++;
    69             addedge(u,v);
    70         }
    71         rep(i,1,n) if(!dfn[i]){
    72             Tarjan(i);
    73         }
    74         rep(i,1,n) num[belong[i]]++;     //求出每个连通分量的点数
    75         rep(u,1,n) for(int i=head[u];~i;i=edge[i].next){
    76             int v=edge[i].to;
    77             int tmp1=belong[u],tmp2=belong[v];
    78             if( tmp1!=tmp2 ){        //缩点后,进行重新构图,并且添加的是反向边,方便进行票数的统计
    79                 addedge1(tmp2,tmp1);outdeg[tmp1]++; 
    80             }
    81         }
    82         int mx=-1;
    83         rep(i,1,scc) if(!outdeg[i]){    //统计所有出度为0的连通分量所能够得到的票数
    84             clr(vis,0);
    85             sum=0;dfs(i);
    86             ans[i]=sum-1;     //获得的票数为路径上所有的点数和-1,因为自己不能获得自己的票
    87             mx=max(mx,ans[i]);
    88         }    
    89         printf("Case %d: %d
    ",++ncase,mx);
    90         bool first=false;
    91         rep(i,1,n) if(ans[belong[i]]==mx){    //将所有票数等于最大票数的点输出
    92             if(!first)printf("%d",i-1);
    93             else printf(" %d",i-1);
    94             first=true;
    95         }puts("");
    96     }
    97 }

    2018-12-01

  • 相关阅读:
    《当程序员的那些狗日日子》(六)继续熬夜学习的日子
    《当程序员的那些狗日日子》(四)喘过气来了
    《当程序员的那些狗日日子》(二)走上不归路
    《当程序员的那些狗日日子》(八)床上等你
    《当程序员的那些狗日日子》(一)毕业后的徘徊
    wince定时拍照功能(转)
    Excel公式找出某一列中是否有某值
    2010年到10月的流水帐
    不错的windows phone的博客
    将同一个表中的一个域更新到另外一个域的SQL文
  • 原文地址:https://www.cnblogs.com/00isok/p/10049925.html
Copyright © 2020-2023  润新知