问题描述:
大学班级选班长,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
解题思路:
Kosaraju算法:
①第一遍 dfs 确定原图的逆后序序列
②第二遍 dfs 在反图中按照逆后序序列进行遍历,每次由起点遍历到的点即构成一个 SCC,为了求解该题,还有一个数组cnt[i用来记录每个SCC里的顶点数。
这样就求出了所有的强连通分量,由题意,要选择投票数最高的,且投票具有传递性,且每一个SCC里的点投票数是相同的,所以得票数最高的SCC必定是原图中出度为0的SCC,这里先缩点后再求解。因为要在反图中DFS,所以原图和反图都要存下来,至于求解得票数最高的SCC可以在缩点后,原图中求出度为0的SCC,或者在反图中求入度为0的SCC,我采用了后者,因为前者也还要在反图中DFS求得票数。
思路:
缩点后,不难发现对于属于第 i 个 SCC 的点来说,答案分为两部分,令 SCC[i] 表示第 i 个 SCC 中点的个数:
①当前 SCC 中的点,ans += SCC[i] – 1(去除自己)
其它 SCC 中的点
②SUM ( SCC[j] ),其中 j 可到达 i
缩点后的反图、原图、反图都用链式前向星存储,反图中SCC入度为0的点可能不止一个,所以得到了所有入度为0的SCC后,要对这些SCC进行DFS,sum[i]记录入度为0的SCC最终的得票数,遍历数组sum,就可以得到最高票数,之后对sum[i]为最高票数的SCC里的点进行输出即可。
实验代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=5050;
int head1[N],head2[N],head[N];
int n,c[N],scnt,dcnt,dfn[N],cnt[N];
//dcnt-dfs序计数,scnt-scc计数,dfn[i]-dfs中后序序列第i个点,c[i]-i号点所在scc编号,cnt第i个子图的元素数
bool vis[N];
int sum[N],ans[N],ansize;
int last[N],lcot;
struct Edge
{
int to;
int next;
};
Edge edge1[30050]; //+
Edge edge2[30050]; //-
Edge edge[30050];
int in_scc[N];
int ecnt1,ecnt2,ecnt;
void insert_edge(int a,int b)
{
edge1[ecnt1].to=b;
edge1[ecnt1].next=head1[a];
head1[a]=ecnt1++;
edge2[ecnt2].to=a;
edge2[ecnt2].next=head2[b];
head2[b]=ecnt2++;
}
void initial()
{
ecnt1=0;
ecnt2=0;
ecnt=0;
for(int i=0;i<=n;i++)
{
head1[i]=-1;
head2[i]=-1;
head[i]=-1;
}
}
void dfs1(int x)
{
vis[x]=1;
int y;
for(int i=head1[x];i!=-1;i=edge1[i].next)
{
y=edge1[i].to;
if(!vis[y])
dfs1(y);
}
dfn[++dcnt]=x;
}
void dfs2(int x)
{
c[x]=scnt;
cnt[scnt]++;
int y;
for(int i=head2[x];i!=-1;i=edge2[i].next)
{
y=edge2[i].to;
if(!c[y])
dfs2(y);
}
}
void suodian()
{
ecnt=0;
for(int i=0;i<=scnt;i++)
in_scc[i]=0;
int j;
for(int i=0;i<n;i++)
{
for(int x=head2[i];x!=-1;x=edge2[x].next)
{
j=edge2[x].to;
if(c[i]!=c[j])
{
edge[ecnt].to=c[j];
edge[ecnt].next=head[c[i]];
head[c[i]]=ecnt++;
in_scc[c[j]]++;
}
}
}
}
void dfs_scc(int x,int k)
{
int y;
vis[x]=1;
for(int i=head[x];i!=-1;i=edge[i].next)
{
y=edge[i].to;
if(!vis[y])
{
sum[k]+=cnt[y];
dfs_scc(y,k);
}
}
}
void kosaraju()
{
//初始化
dcnt=scnt=0;
for(int i=0;i<=n;i++)
{
c[i]=0;
vis[i]=0;
cnt[i]=0;
}
for(int i=0;i<n;i++)
if(!vis[i]) dfs1(i);
for(int i=n;i>=1;i--)
if(!c[dfn[i]]) ++scnt,dfs2(dfn[i]);
suodian();
int ansize=0;
for(int i=1;i<=scnt;i++)
{
if(in_scc[i]==0)
ans[ansize++]=i;
}
int scc;
for(int i=0;i<ansize;i++)
{
scc=ans[i];
sum[i]=cnt[scc]-1;
for(int j=0;j<=scnt;j++)
vis[j]=0;
dfs_scc(scc,i);
}
int piao=0;
for(int i=0;i<ansize;i++)
if(sum[i]>piao) piao=sum[i];
cout<<piao<<endl;
lcot=0;
for(int i=0;i<ansize;i++)
{
if(sum[i]==piao)
for(int j=0;j<n;j++)
{
if(c[j]==ans[i])
{
last[lcot++]=j;
}
}
}
sort(last,last+lcot);
for(int i=0;i<lcot;i++)
{
if(i<lcot-1)
cout<<last[i]<<" ";
else
cout<<last[i]<<endl;
}
}
int main(void)
{
int T,m;
scanf("%d",&T);
int a,b;
int cot=0;
for(int i=0;i<T;i++)
{
scanf("%d%d",&n,&m);
initial();
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
insert_edge(a,b);
}
cot++;
cout<<"Case "<<cot<<": ";
kosaraju();
}
return 0;
}