题目
大学班级选班长,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算法,要先找出所有SCC,然后缩点,再基于SCC来计算班长票数。
找到有向图中所有的 SCC
- 第一遍 dfs 确定原图的逆后序序列
- 第二遍 dfs 在反图中按照逆后序序列进行遍历,每次遍历到的所有点构成一个强连通分量。
缩点后,可以发现对于属于第 i 个 SCC 点来说,答案分为两部分,令 SCC[i] 表示第 i 个 SCC 中点的个数
• 当前 SCC 中的点,ans += SCC[i] – 1(去除自己)
• 其它 SCC 中的点 SUM ( SCC[可达i] )
最后答案一定出现在出度为 0 的 SCC中。
心得
这个题目比较复杂,可能单独把SCC的算法拿出来会比较好理解。但是这个题目不仅要算SCC,还要用来做进一步计算,对我来说比较复杂了。最近学的新东西太多了,我觉得自己需要对这些类别完整地整理一遍,再自己多找几个题目做一下,不能浪费在这门课上花费的精力。
!越来越难了,我选择放弃,先搞面试缓上几天…
代码
#include <algorithm>
#include <cstring>
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
int sccid[5010]; //记录每个点所在的连通分量编号
vector<int> graph[5010];
int pre[5010], low[5010], sz[5010], dfs_clock, k;
stack<int> s;
void dfs(int u) {
pre[u] = low[u] = ++dfs_clock;
s.push(u);
for (int i = 0; i < graph[u].size(); i++) {
int v = graph[u][i];
if (!pre[v]) {
dfs(v);
low[u] = min(low[u], low[v]);
} else if (!sccid[v])
low[u] = min(low[u], low[v]);
}
if (pre[u] == low[u]) {
k++;
for (;;) {
int x = s.top();
s.pop();
sccid[x] = k;
sz[k]++;
if (x == u) break;
}
}
}
//反向遍历
vector<int> rgraph[5010];
int visited[5010]; //访问标记
int rdfs(int u) {
int ans = sz[u];
visited[u] = true;
for (int i = 0; i < rgraph[u].size(); i++) {
int v = rgraph[u][i];
if (visited[v]) continue;
ans += rdfs(v);
}
return ans;
}
//计算SCC
void scc(int n) {
dfs_clock = k = 0;
memset(pre, 0, sizeof(pre));
memset(sccid, 0, sizeof(sccid));
memset(sz, 0, sizeof(sz));
for (int i = 0; i < n; i++) {
if (pre[i] == 0)
dfs(i);
}
}
int degs[5010];
int support[5010];
int main() {
int t;
scanf("%d", &t);
for (int j = 1; j <= t; j++) {
int n, m;
scanf("%d %d", &n, &m);
//清空
{
vector<int> graphe[5010], rgraphe[5010];
swap(graph, graphe);
swap(rgraphe, rgraph);
}
for (int i = 0; i < m; i++) {
int x, y;
scanf("%d %d", &x, &y);
graph[x].push_back(y);
}
scc(n);
//缩点
memset(degs, 0, sizeof(degs));
for (int i = 0; i < n; i++) {
for (int j = 0; j < graph[i].size(); j++) {
int u = sccid[i], v = sccid[graph[i][j]];
if (u == v) continue;
degs[u]++;
rgraph[v].push_back(u);
}
}
int mx = 0;
for (int i = 1; i <= k; i++) {
if (degs[i] == 0) {
memset(visited, 0, sizeof(visited));
support[i] = rdfs(i); //记录每个点连通的点数
mx = max(mx, support[i]); //记录最大值
}
}
//输出结果
printf("Case %d: %d\n", j, mx - 1);
bool flag = false;
for (int i = 0; i < n; i++) {
int u = sccid[i];
if (degs[u] == 0 && support[u] == mx) {
if (flag) printf(" ");
printf("%d", i);
flag = true;
}
}
printf("\n");
}
return 0;
}