程序设计思维Week8-作业
C-班长竞选
Description
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适。勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。
对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!
Sample
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
Idea
前提:用前向星存储图
- 用Kosaraju求SCC
① 第一遍DFS遍历原图,确定原图的后序序列
② 将后序序列变成逆后序序列,第二遍DFS按照逆后序序列遍历反图(各边倒转),每次由起点遍历到的点构成一个SCC,遍历过程中记录每个SCC中点的数目。 - 缩点
将同一个SCC的点合并成一个点,遍历反图中的每个点的边,如果边的两端不在同一SCC中则加入该边,最终形成新的缩点后的图。 - 计算每个SCC获得的票数
前提:同一SCC中的点获得票数相同
遍历缩点后的图,对于入度为0的点(缩点后的图源于反图,所以在原图就是出度为0的点,所有可以到达它的点,都给它投票),进行第三遍DFS计算该点总共获得的票数(去除自己) - 计算所有SCC中的最高票数
- 对拥有最高票数的SCC标记
- 遍历原图的所有点,如果某点在被标记的SCC中,则输出该点(获得最高票数)
Summary
这道题很综合,属实复杂,基本方法是前向星存图+Kosaraju求SCC+DFS。
整个过程中,共需要三张图——原图、反图、缩点后的图,三遍DFS,用到的变量、数组很多所以要注意初始化以及输出格式。
要注意N个同学的序号是0~N-1
Codes
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 5010;
const int maxm = 30020;
const int inf = 1e9;
int T, n, m, tot = 0, tot1 = 0, tot2 = 0, dcnt = 0, scnt = 0,temp=0;
int head[maxn],head1[maxn],head2[maxn], vis[maxn], dfn[maxn],c[maxn],scc[maxn],in_deg[maxn],flag[maxn],ans[maxn],maxSCC[maxn];
struct edge {
int u, v, next;
}e1[maxm],e2[maxm],e[maxm];
//原图
void add1(int u, int v) {
e1[++tot1].u = u;
e1[tot1].v = v;
e1[tot1].next = head1[u];
head1[u] = tot1;
}
//反图
void add2(int u, int v) {
e2[++tot2].u = u;
e2[tot2].v = v;
e2[tot2].next = head2[u];
head2[u] = tot2;
}
//缩点
void add(int u, int v) {
e[++tot].u = u;
e[tot].v = v;
e[tot].next = head[u];
head[u] = tot;
in_deg[v]++;
}
void dfs1(int x) {
vis[x] = 1;
for (int i = head1[x]; i != 0; i = e1[i].next)
{
int y = e1[i].v;
if (!vis[y]) {
dfs1(y);
}
}
dfn[++dcnt] = x; //后序
}
//在反图中按照逆后序遍历构成 SCC
void dfs2(int x) {
c[x] = scnt;
scc[scnt]++;
for (int i = head2[x]; i != 0; i = e2[i].next)
{
int y = e2[i].v;
if (!c[y])dfs2(y);
}
}
void kosaraju() {
dcnt = 0, scnt = 0;
memset(c, 0, sizeof(c));
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i++)
if (!vis[i])dfs1(i);
//cout << endl;
//逆后序
for (int i = n; i >= 1; i--)
{
//cout << dfn[i] << endl;
if (!c[dfn[i]])++scnt, dfs2(dfn[i]);
}
}
//缩点图
void together() {
for (int x = 1; x <= n; x++) {
for (int i = head2[x]; i != 0; i = e2[i].next) {
int y = e2[i].v;
if (c[x] == c[y])continue;
add(c[x], c[y]);
}
}
}
void dfs(int x) {
temp += scc[x];
flag[x] = 1;
for (int i = head[x]; i != 0; i = e[i].next)
{
int y = e[i].v;
if(!flag[y])dfs(y);
}
}
void init() {
for (int i = 0; i <= n; i++) {
head[i] = 0;
head1[i] = 0;
head2[i] = 0;
tot = 0;
tot1 = 0;
tot2 = 0;
dcnt = 0, scnt = 0, temp = 0;
vis[i] = 0;
dfn[i] = 0;
scc[i] = 0;
c[i] = 0;
in_deg[i] = 0;
flag[i] = 0;
ans[i] = 0;
maxSCC[i] = 0;
}
}
int main()
{
cin.sync_with_stdio(false);
cin >> T;
int num = 1;
while (T--) {
cin >> n >> m;
init();
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
a++, b++;
add1(a, b);
add2(b, a);
}
kosaraju();
together();
//ans = 0;
for (int i = 1; i <= scnt; i++) {
if (in_deg[i]==0) {
temp = 0;
memset(flag, 0, sizeof(flag));
//ans[i] += scc[i] - 1;
dfs(i);
ans[i] += (temp - 1);
}
}
int maxans = 0;
for (int i = 1; i <= scnt; i++)
if (maxans < ans[i]) { maxans = ans[i]; }
for (int i = 1; i <= scnt; i++) {
if (ans[i] == maxans)maxSCC[i]=1;
}
printf("Case %d: %d\n", num, maxans);
int pos = 0;
for (int i = 1; i <= n; i++) {
if (maxSCC[c[i]]) {
if (pos == 0) { cout << i - 1; pos++; }
else cout << " " << i - 1;
}
}
cout << endl;
num++;
}
}