题目
大学班级选班长,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[i] 为第 i 个强连通分量中点的个数,对于属于第 i 个强连通分量的点来说,答案分为两部分:
当前强联通分量中的点,SCC[i] – 1(去除自己)
其它强联通分量中的点,sum(SCC[j]),其中 j 为可到达 i 的强连通分量
最后答案一定出现在出度为 0 的强联通分量中,因此对新图中每个出度为 0 的点进行 dfs,计算其能到达的点的 SUM(SCC[j]),即可得到答案。
代码
#include<iostream>
#include<vector>
#include<string.h>
using namespace std;
const int N = 5005;
const int M = 30005;
struct edge
{
int to;
int next;
};
int head1[N], head2[N], head3[N], dfn[N], c[N], vis[N];
int scc[N], ans[N], outdeg[N];
int dcnt, scnt;
edge e1[M], e2[M], e3[M];
int tot, tot3;
int n, m, x;
void add(int u, int v)
{
e1[++tot].to = v;
e1[tot].next = head1[u];
head1[u] = tot;
e2[tot].to = u;
e2[tot].next = head2[v];
head2[v] = tot;
}
void add3(int u, int v)
{
e3[tot3].to = v;
e3[tot3].next = head3[u];
head3[u] = tot3;
tot3++;
}
void dfs1(int s)
{
vis[s] = 1;
for (int i = head1[s]; i != -1; i = e1[i].next)
{
int v = e1[i].to;
if (vis[v] == 0)
{
dfs1(v);
}
}
dfn[++dcnt] = s;
}
void dfs2(int s)
{
c[s] = scnt;
scc[scnt]++;
for (int i = head2[s]; i != -1; i = e2[i].next)
{
int v = e2[i].to;
if (c[v] == 0)
dfs2(v);
}
}
void dfs3(int s)
{
vis[s] = 1;
x += scc[s];
for (int i = head3[s]; i != -1; i = e3[i].next)
{
int v=e3[i].to;
if (!vis[v])
dfs3(v);
}
}
void kosaraju()
{
dcnt = scnt = 0;
for (int i = 1; i <= n; i++)
if (vis[i] == 0)
dfs1(i);
for (int i = n; i >= 1; i--)
if (c[dfn[i]] == 0) ++scnt,dfs2(dfn[i]);
}
void ini()
{
tot3 = 0,tot = 0;
memset(head1, -1, sizeof(head1));
memset(head2, -1, sizeof(head2));
memset(head3, -1, sizeof(head3));
memset(outdeg, 0, sizeof(outdeg));
memset(c, 0, sizeof(c));
memset(vis, 0, sizeof(vis));
memset(scc, 0, sizeof(scc));
memset(ans, 0, sizeof(ans));
}
int main()
{
int t;
scanf("%d", &t);
for (int i = 1; i <= t; i++)
{
scanf("%d%d", &n, &m);
ini();
while (m--)
{
int a, b;
scanf("%d%d", &a, &b);
a++,b++;
add(a, b);
}
kosaraju();
for (int j = 1; j <= n; j++)
{
for (int k = head1[j]; k != -1; k = e1[k].next)
{
if (c[j] != c[e1[k].to])
{
add3(c[e1[k].to], c[j]);
outdeg[c[j]]++;
}
}
}
int max = 0;
for (int j = 1; j <= scnt; j++)
{
if (outdeg[j] == 0)
{
x = 0;
memset(vis, 0, sizeof(vis));
dfs3(j);
ans[j] = x;
if (x > max)
max = x;
}
}
printf("Case %d: %d\n", i, max - 1);
bool flag = true;
for (int j = 1; j <= n; j++)
{
if (ans[c[j]] == max)
{
if (flag)
flag = false, printf("%d", j - 1);
else
printf(" %d", j - 1);
}
}
printf("\n");
}
return 0;
}