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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3639

借鉴了一位大佬的博客:https://blog.csdn.net/u013480600/article/details/32140501

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

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

结论:原图中具有最多粉丝的点一定在新图的那些出度为0的点所代表的分量中.

证明:假设u节点粉丝最多且它所属的分量出度不为0,那么u节点一定是某个节点v的粉丝,所以v的粉丝必然包含了u的所有粉丝加上u本身.所以v的粉丝必然多余u.由此矛盾.

下面的问题是如何找新DAG图的每个节点(所代表分量中的原节点)的最大粉丝数? 该粉丝数=本连通分量的点数-1+本连通分量能通过ß这种边逆向走到的所有分量的点数和. 所以我们直接建立缩点树的逆图DAG即可,如果u->v表示u分量将获得v分量的所有节点作为粉丝.所以我们只需要对那几个入度为0的点做DFS即可.

每次DFS到一个新节点,该点所代表的分量节点数就都加到sum上去,表示新加了很多粉丝.最后找最大粉丝值的分量点输出即可.

特殊案例:
1
6 6
0 1
0 2
1 5
1 3
2 3
2 4

输出是
3 3

该案例的反向图:

切记不要把0号点的粉丝数算了两遍,进行dfs的时候要进行标记,一开始一直wrong在这里

代码写得有点累赘:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <map>
#include <stack>
#include <queue>
using namespace std;

const int maxn =5000+10;
int n,m,T;
int pre[maxn],low[maxn],sccno[maxn],dfs_clock,scc_cnt,w[maxn],outdeg[maxn],ans,d[maxn],flag[maxn],vis[maxn];
stack<int>S;
vector<int>G[maxn],mp[maxn];



void dfs(int u)
{
    pre[u] = low[u] =++dfs_clock;
    S.push(u);
    for(int i = 0; i<G[u].size(); i++)
    {
        int v = G[u][i];

        if(!pre[v])
        {
            dfs(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++;
        int cnt =0;
        for(; ;)
        {
            cnt++;
            int x = S.top();
            S.pop();
            sccno[x] = scc_cnt;
            if(x==u)
                break;
        }
        w[scc_cnt]=cnt;
    }
}


void find_scc()
{
    dfs_clock = scc_cnt =0;
    memset(sccno,0,sizeof(sccno));
    memset(outdeg,0,sizeof(outdeg));
    memset(d,0,sizeof(d));
    memset(flag,0,sizeof(flag));
    memset(w,0,sizeof(w));
    memset(pre,0,sizeof(pre));
    for(int i=0; i<n; i++)
        if(!pre[i])
            dfs(i);
}

int dfs2(int u)
{
    vis[u]=1;
      d[u]=w[u];
    for(int i=0; i<mp[u].size(); i++)
    {
        int v=mp[u][i];
        if(!vis[v])
          d[u]+=dfs2(v);
    }
    return d[u];
}

void build()
{
    for(int i=1; i<=scc_cnt; i++)
        mp[i].clear();
    for(int u=0; u<n; u++)
    {
        for(int i=0; i<G[u].size(); i++)
        {
            int v=G[u][i];
            if(sccno[u]!=sccno[v])
            {

                outdeg[sccno[u]]++;
                mp[sccno[v]].push_back(sccno[u]);//逆向建图
            }
        }
    }
}



int main()
{
    int kase=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);

        for(int i=0; i<n; i++)
            G[i].clear();

        int u,v;
        for(int i=0; i<m; i++)
        {
            scanf("%d %d",&u,&v);
            G[u].push_back(v);
        }

        find_scc();//缩点


        build();//建图


        vector<int>O_deg;//保存出度为0的点

        for(int i=1; i<=scc_cnt; i++)
        {
            if(outdeg[i]==0)
            {
                O_deg.push_back(i);
            }
        }


        int max_sum=-1;
        for(int i=0; i<O_deg.size(); i++)//
        {
            memset(vis,0,sizeof(vis));//避免重复计算
            max_sum=max(max_sum,dfs2(O_deg[i]));
        }


        for(int i=1; i<=scc_cnt; i++)
        {
            if(d[i]==max_sum)
                flag[i]=1;
        }

           vector<int>s;
        for(int i=0; i<n; i++)
        {
            if(flag[sccno[i]])
                s.push_back(i);
        }
        ans=max_sum-1;

        printf("Case %d: %d\n",++kase,ans);

        for(int i=0; i<s.size(); i++)
        {
            if(i!=0)
                printf(" %d",s[i]);
            else
                printf("%d",s[i]);
        }
        printf("\n");

    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值