题解:洛谷 P2419 Cow Contest

【题目来源】

洛谷:P2419 [USACO08JAN] Cow Contest S - 洛谷

【题目描述】

FJ 的 \(N\)\(1 \leq N \leq 100\))头奶牛们最近参加了场程序设计竞赛。在赛场上,奶牛们按 \(1, 2, \cdots, N\) 依次编号。每头奶牛的编程能力不尽相同,并且没有哪两头奶牛的水平不相上下,也就是说,奶牛们的编程能力有明确的排名。整个比赛被分成了若干轮,每一轮是两头指定编号的奶牛的对决。如果编号为 \(A\) 的奶牛的编程能力强于编号为 \(B\) 的奶牛(\(1 \leq A, B \leq N\)\(A \neq B\)),那么她们的对决中,编号为 \(A\) 的奶牛总是能胜出。FJ 想知道奶牛们编程能力的具体排名,于是他找来了奶牛们所有 \(M\)\(1 \leq M \leq 4,500\))轮比赛的结果,希望你能根据这些信息,推断出尽可能多的奶牛的编程能力排名。比赛结果保证不会自相矛盾。

【输入】

第一行两个用空格隔开的整数 \(N, M\)

\(2\sim M + 1\) 行,每行为两个用空格隔开的整数 \(A, B\) ,描述了参加某一轮比赛的奶牛的编号,以及结果(每行的第一个数的奶牛为胜者)。

【输出】

输出一行一个整数,表示排名可以确定的奶牛的数目。

【输入样例】

5 5
4 3
4 2
3 2
1 2
2 5

【输出样例】

2

【核心思想】

  1. 问题分析:给定 \(N\) 头奶牛和 \(M\) 场比赛结果(\(A\)\(B\) 表示 \(A\) 能力 \(> B\)),求排名可以完全确定的奶牛数量。排名可确定意味着该奶牛与其他 \(N-1\) 头奶牛的关系都是已知的(直接或间接)。这是一个传递闭包(Floyd-Warshall)问题,关键在于利用比赛结果的传递性,通过Floyd算法计算所有可达关系。

  2. 算法选择

    • Floyd-Warshall 传递闭包\(d[i][j] = 1\) 表示 \(i\) 的能力 \(> j\)\(i\) 可到达 \(j\))。若 \(i > k\)\(k > j\),则 \(i > j\)
    • 排名可确定性判定:奶牛 \(i\) 排名可确定 \(\Leftrightarrow\) 对于其他任意奶牛 \(j\)\(i > j\)\(j > i\) 至少一个成立,即 \(i\) 与其他 \(N-1\) 头奶牛都有确定的强弱关系
  3. 关键步骤

    • 初始化\(d[i][j] = 1\)\(i\) 直接战胜 \(j\)
    • Floyd 传递闭包\(k, i, j\) 三重循环):
      • d[i][k] && d[k][j],则 d[i][j] = 1
    • 统计可确定排名的奶牛(遍历 \(i\)\(1\)\(N\)):
      • \(s = \sum_{j=1}^{N}(d[j][i] + d[i][j])\)
      • \(s = N - 1\),则 ans++\(i\) 与其他 \(N-1\) 头都有确定关系)
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^3)\),Floyd 三重循环
    • 空间复杂度:\(O(N^2)\),邻接矩阵
  5. 传递闭包判定排名的核心思想

    • 比赛结果的图建模:将每场比赛结果 \(A\)\(B\) 建模为有向边 \(A \to B\),表示 \(A\) 强于 \(B\)
    • 传递性的利用:若 \(A > B\)\(B > C\),则 \(A > C\)。Floyd 算法通过中转点 \(k\) 枚举所有间接关系,补全缺失的边
    • 排名确定的条件:一头奶牛的排名完全确定,当且仅当它与所有其他奶牛都有直接或间接的胜负关系(即在全序关系中位置固定)
    • 计数技巧\(d[j][i] + d[i][j]\) 表示 \(i\)\(j\) 是否有确定关系(至少一个方向可达)。\(s = N-1\) 意味着 \(i\) 与其他 \(N-1\) 头都有关系(注意 \(d[i][i]\) 不计入)
    • 适用于"偏序关系补全为全序,统计全序中位置确定的元素"问题

【解题思路】

【算法标签】

普及 #Floyd

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 105;           // 最大顶点数
int n, m;                    // n: 顶点数, m: 边数
int d[N][N];                 // 邻接矩阵,d[i][j]=1表示i可到达j
int ans;                     // 满足条件的顶点个数

/**
 * 传递闭包算法
 * 基于Floyd-Warshall思想,计算有向图的可达性
 * 如果i可到达k,且k可到达j,则i可到达j
 */
void floyd()
{
    // 三重循环,k为中转点
    for (int k = 1; k <= n; k++)
    {
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                // 如果i可到达k,且k可到达j,则i可到达j
                if (d[i][k] && d[k][j])
                {
                    d[i][j] = 1;  // 标记i可到达j
                }
            }
        }
    }
}

int main()
{
    // 输入顶点数和边数
    cin >> n >> m;

    // 构建有向图
    for (int i = 1; i <= m; i++)
    {
        int x, y;
        cin >> x >> y;
        d[x][y] = 1;  // 有向边x→y
    }

    // 计算传递闭包
    floyd();

    // 统计满足条件的顶点个数
    for (int i = 1; i <= n; i++)
    {
        int s = 0;  // 统计与顶点i有可达关系的顶点数

        // 计算到达i的顶点数 + i到达的顶点数
        for (int j = 1; j <= n; j++)
        {
            s += d[j][i];  // 统计有多少顶点可到达i
            s += d[i][j];  // 统计i可到达多少顶点
        }

        // 如果i与其他n-1个顶点都有可达关系,计数加1
        if (s == n - 1)
        {
            ans++;
        }
    }

    // 输出满足条件的顶点个数
    cout << ans << endl;

    return 0;
}

【运行结果】

5 5
4 3
4 2
3 2
1 2
2 5
2
posted @ 2026-08-10 12:28  团爸讲算法  阅读(13)  评论(0)    收藏  举报