班长竞选(kosaraju缩点)

问题描述

大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?

Input

本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。

Output

对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!

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

解题思路

开始看到这个题以为是单纯拓扑序列来做就可以,后来发现很容易出现环,并且环内各个成员相互投票,就说明某个成员的票数就等于环内成员数量-1。所以这个题要求我们先缩点,将每个强连通分量缩成一个点后,就不再有环。

设某个强连通分量内元素的个数是 s c c [ i ] scc[i] ,缩点后,我们不难发现,对于属于第 i i s c c scc 的点来说, s u m = s c c [ i ] 1 + s u m [ s c c [ j ] ] sum=scc[i]-1+sum[scc[j]] ,其中 j j 能到达 i i

所以最终答案一定出现在出度为0的 s c c scc 中,我们可以将每条边反向,然后对入度为0的点进行dfs,计算他能到达所有的点k,最终结果就是 s c c [ i ] 1 + s u m [ s c c [ k ] ] scc[i]-1+sum[scc[k]] 。然后比较所有出度为0的点即可。

要注意的是,最终结果还要顺序输出所有的得票最高的同学,我们可以使用多个priority_queue来存每个scc中成员的负数,然后最终比较的时候统计进入另外一个priority_queue中即可。

完整代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

const int maxn=5000+10;
const int maxm=30000+10;
struct node
{
    int next,to;
};
node edge[maxm],edge2[maxm],edge3[maxn];//原图,反图,缩点反图
int t,n,m,cnt,cnt2,cnt3,fcnt,ccnt,sum,ans;
int head[maxn],head2[maxn],head3[maxn],f[maxn],ff[maxn],color[maxn],scc[maxn],in_deg[maxn];
bool vis[maxn];
priority_queue<int> q[maxn],q2;
inline void add(node* _edge,int* _head,int x,int y,int& _cnt)
{
    _cnt++;
    _edge[_cnt].to=y;
    _edge[_cnt].next=_head[x];
    _head[x]=_cnt;
}
void dfs1(int x)//确定逆后序序列
{
    vis[x]=true;
    for (int i=head[x]; i!=-1; i=edge[i].next){
        if(!vis[edge[i].to])
            dfs1(edge[i].to);
    }
    f[x]=++fcnt;
}
void dfs2(int x)//反图按照逆后序遍历染色,顺便统计每种颜色的点的个数
{
    color[x]=ccnt; scc[ccnt]++; q[ccnt].push(-x);
    for (int i=head2[x]; i!=-1; i=edge2[i].next){
        if(!color[edge2[i].to])
            dfs2(edge2[i].to);
    }
}
void dfs3(int x)
{
    vis[x]=true;
    for (int i=head3[x]; i!=-1; i=edge3[i].next){
        if(!vis[edge3[i].to])
        {
            sum+=scc[edge3[i].to];
            dfs3(edge3[i].to);
        }
    }
}
inline void kosaraju()//找scc
{
    ccnt=fcnt=0;
    for (int i=1; i<=n; i++) if(!vis[i]) dfs1(i);
    for (int i=1; i<=n; i++) ff[f[i]]=i;
    for (int i=n; i>=1; i--){
        if(!color[ff[i]]){
            ++ccnt;
            dfs2(ff[i]);
        }
    }
}
inline void indent()//缩点反向重构,缩图后,图中所有的结点都是颜色
{
    for (int i=1; i<=n; i++){
        for (int j=head2[i]; j!=-1; j=edge2[j].next){
            if(color[i]!=color[edge2[j].to]){
                add(edge3,head3,color[i],color[edge2[j].to],cnt3);
                in_deg[color[edge2[j].to]]++;//j的入度+1
            }
        }
    }
}
inline void init()//初始化
{
    cnt=cnt2=cnt3=ans=sum=fcnt=ccnt=0;
    memset(in_deg,0,sizeof(in_deg));
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    memset(head3,-1,sizeof(head3));
    memset(vis,false,sizeof(vis));
    memset(color,0,sizeof(color));
    memset(scc,0,sizeof(scc));
    memset(f,0,sizeof(f));
    while(!q2.empty()) q2.pop();
    for (int i=1; i<=n; i++) {
        while(!q[i].empty())
            q[i].pop();
    }

}
inline void solve()//在缩完的图中进行最终求解
{
    for (int i=1; i<=ccnt; i++){
        if(in_deg[i]==0){
            sum=scc[i]-1;
            memset(vis,false,sizeof(vis));
            dfs3(i);
            if(ans<sum){
                while(!q2.empty()) q2.pop();
                while(!q[i].empty()){
                    q2.push(q[i].top());
                    q[i].pop();
                }
                ans=sum;
            }
            else if(ans==sum){
                while(!q[i].empty()){
                    q2.push(q[i].top());
                    q[i].pop();
                }
            }
        }
    }
}
inline void print(int _tt)
{
    printf("Case %d: %d\n%d",_tt,ans,-q2.top()-1);q2.pop();
    while(!q2.empty()){ printf(" %d",-q2.top()-1); q2.pop();}
    printf("\n");
}
int getint()
{
    int x=0,s=1;
    char ch=' ';
    while(ch<'0' || ch>'9'){
        ch=getchar();
        if(ch=='-') s=-1;
    }
    while(ch>='0' && ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*s;
}
int main()
{
    t=getint();
    for (int tt=1; tt<=t; tt++){
        init();
        n=getint(); m=getint();
        for (int i=1; i<=m; i++){
            int a=getint(); int b=getint();
            add(edge,head,a+1,b+1,cnt);
            add(edge2,head2,b+1,a+1,cnt2);//0到n-1变成1到n
        }
        kosaraju();
        indent();
        solve();
        print(tt);
    }
    return 0;
}
发布了45 篇原创文章 · 获赞 39 · 访问量 4529

猜你喜欢

转载自blog.csdn.net/weixin_43347376/article/details/105572110