League(拓扑排序)
题意
\(n\)个人进行\(\frac{n(n -1)}{2}\)场比赛,每个人都要以一个特定的顺序与其他人比赛。
且每个人每天只可以比一场比赛,问最少比赛的天数为多少,无解输出\(-1\)。
题目链接:https://atcoder.jp/contests/abc139/tasks/abc139_e
数据范围
\(3 \leq n \leq 1000\)
思路
对于第\(i\)个人的对手\(u_1, u_2, \dots, u_{n-1}\),我们不妨令\((i, u_k)\)表示\(i\)与\(u_k\)进行比赛。
我们可以将题意简化为,\((i, u_{k+1})\)比\((i, u_k)\)至少晚一天进行。
因此,我们可以将比赛顺序抽象为一个拓扑图,其中节点为一场比赛(如\((i, u_k)\)),边表示时间限制。
如果有环的话,则无解;否则为拓扑图上的最长链问题。以上可以通过拓扑排序解决。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1010, M = N * N;
int n;
int g[N][N];
int h[M], e[M], ne[M], idx;
int d[M], t[M];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
int id(int a, int b)
{
if(a > b) swap(a, b);
return (a - 1) * n + b;
}
int main()
{
scanf("%d", &n);
memset(h, -1, sizeof h);
for(int i = 1; i <= n; i ++) {
for(int j = 1; j < n; j ++) {
int x;
scanf("%d", &x);
g[i][j] = x;
}
}
for(int i = 1; i <= n; i ++) {
for(int j = 1; j < n - 1; j ++) {
int a = id(i, g[i][j]), b = id(i, g[i][j + 1]);
add(a, b), d[b] ++;
}
}
queue<int> que;
for(int i = 1; i <= n * n; i ++) {
if(!d[i]) {
que.push(i);
t[i] = 1;
}
}
while(que.size()) {
int tt = que.front();
que.pop();
for(int i = h[tt]; ~i; i = ne[i]) {
int j = e[i];
if(--d[j] == 0) {
que.push(j);
t[j] = t[tt] + 1;
}
}
}
int ans = 0;
for(int i = 1; i <= n * n; i ++) ans = max(ans, t[i]);
for(int i = 1; i <= n * n; i ++) {
if(d[i]) {
ans = -1;
}
}
printf("%d\n", ans);
return 0;
}