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

样例
Input
2
4 3
3 2
2 0
2 1
3 3
1 0
2 1
0 2
Output
Case 1: 2
0 1
Case 2: 2
0 1 2
思路分析
本题方法:求出SCC并缩点, 计算每个SCC票数。
求SCC:Kosaraju
第一遍dfs遍历原图,得到逆后序序列。
第二遍dfs在反图中按逆后序序列遍历,每次由起点遍历到的点构成一个SCC,在遍历同时记录该SCC中点数。
缩点:
将一个SCC看作一个点,建立新图。不在同一个SCC中的两个点u、v,且u可到达v,SCC编号为c[u],c[v],将边(c[u],c[v])插入到新图中。同时得到新图中每个点的入度。
求票数:
对第i个SCC来说,其中点的票数等于=第i个SCC中的点数+所有能到达i的SCC中的点数-1。用dfs遍历得到每个SCC的点的票数(同一个SCC中点两两相连,票数相同),找到票数最高的SCC,其中的点就是票数最高的点。
注意输出要求
AC代码
#include<iostream>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
int head[5005],head1[5005],head2[5005];
int vis[5005],vis2[5005],dfn[5005],c[5005],scc[5005],in_deg[5005],ans[5005],sum[5005];
struct Edge
{
int v,next;//相邻的点,权值,下条边的序号
} ;
int tot,tot1,tot2,dcnt,scnt,n,m,temp;
Edge edge[50000],edge1[50000],edge2[50000];
void init()
{
//初始化
tot=0;tot1=0;tot2=0;
memset(head,-1,sizeof(head));
memset(head1,-1,sizeof(head));
memset(head2,-1,sizeof(head));
memset(vis,0,sizeof(vis));
memset(vis2,0,sizeof(vis2));
memset(dfn,0,sizeof(dfn));
memset(c,0,sizeof(c));
memset(scc,0,sizeof(scc));
memset(in_deg,0,sizeof(in_deg));
memset(ans,0,sizeof(ans));
memset(sum,0,sizeof(sum));
}
void add(int u,int v)
{
edge[tot].v=v;
edge[tot].next=head[u];
head[u]=tot;
tot++;
}
void add1(int u,int v)
{
edge1[tot1].v=v;
edge1[tot1].next=head1[u];
head1[u]=tot1;
tot1++;
}
void add2(int u,int v)
{
edge2[tot2].v=v;
edge2[tot2].next=head2[u];
head2[u]=tot2;
tot2++;
}
void dfs(int u)
{
vis[u]=1;
for (int i= head[u];~i;i= edge[i].next)
{
int v=edge[i].v;
if(vis[v]==0)
{
dfs(v);
}
}
dfn[dcnt++]=u;
}
void dfs1(int u)
{
c[u]=scnt;
scc[scnt]++; //scc中点数
for (int i= head1[u];~i;i= edge1[i].next)
{
int v=edge1[i].v;
if(c[v]==0)
{
dfs1(v);
}
}
}
void dfs2(int u)
{
vis2[u]=1;
temp+=scc[u];
for (int i= head2[u];~i;i=edge2[i].next)
{
int v=edge2[i].v;
if(vis2[v]==0)
{
dfs2(v);
}
}
}
void kosaraju()
{
dcnt=0;
scnt=0;
for(int i=0;i<n; i++)
{
if(!vis[i]) dfs(i);
}
for(int i=n-1; i>=0; i--)
{
if(!c[dfn[i]])
{
++scnt;
dfs1(dfn[i]);
}
}
}
int main()
{
ios::sync_with_stdio(false);
int T;
cin>>T;
for(int count=1;count<=T;count++)
{
init();//初始化
cin>>n>>m;
int A,B;
for(int i=0;i<m;i++)
{
cin>>A>>B;
add(A,B);//原图
add1(B,A);//反图
}
kosaraju();
//缩点
for (int u=0;u<n;u++)
{
for (int i=head1[u];~i;i=edge1[i].next)
{
int v=edge1[i].v;
if(c[u]==c[v]) continue;//在同一个SCC中
add2(c[u],c[v]);//缩点后的图
in_deg[c[v]]++;//入度
}
}
int Max=0;
for(int i=1;i<=scnt;i++)
{
if(in_deg[i]==0)
{
memset(vis2,0,sizeof(vis2));//注意!!!
temp=0;
dfs2(i);
sum[i]=temp-1;
//cout<<"in_deg "<<i<<" "<<sum[i]<<endl;
Max=max(Max,sum[i]);
}
}
for(int i=1;i<=scnt;i++)
{
if(sum[i]==Max)
{
//cout<<"ans "<<i<<endl;
ans[i]=1;//标记最大
}
}
cout<<"Case "<<count<<": "<<Max<<endl;
bool flag=false;
for(int i=0;i<n;i++)
{
//cout<<i<<" "<<c[i]<<" "<<ans[c[i]]<<endl;
if(ans[c[i]])
{
if(!flag)
{
cout<<i;
flag=true;
}
else
{
cout<<" "<<i;
}
}
}
cout<<endl;
}
return 0;
}
本文介绍了一种解决大学班长选举问题的算法,通过构建图论中的强连通分量(SCC)并进行缩点,计算每个SCC的票数,最终确定得票最多的候选人。使用Kosaraju算法求解SCC,再通过DFS遍历计算票数。
181

被折叠的 条评论
为什么被折叠?



