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;
}
posted @ 2022-06-22 19:40  pbc的成长之路  阅读(74)  评论(0编辑  收藏  举报