题解:洛谷 P5831 Cow Gymnastics

【题目来源】

洛谷:P5831 [USACO19DEC] Cow Gymnastics B - 洛谷

【题目描述】

为了提高健康水平,奶牛们开始进行体操训练了!Farmer John 选定了他最喜爱的奶牛 Bessie 来执教其他 \(N\) 头奶牛,同时评估她们学习不同的体操技术的进度。

\(K\)次训练课的每一次,Bessie 都会根据 \(N\) 头奶牛的表现给她们进行排名。之后,她对这些排名的一致性产生了好奇。称一对不同的奶牛是一致的,如果其中一头奶牛在每次训练课中都表现得都比另一头要好。

请帮助 Bessie 计算一致的奶牛的对数。

【输入】

输入的第一行包含两个正整数 \(K\)\(N\)。以下 \(K\) 行每行包含整数 \(1\dots N\)的某种排列,表示奶牛们的排名(奶牛们用编号 \(1\dots N\) 进行区分)。如果在某一行中 \(A\) 出现在 \(B\) 之前,表示奶牛 \(A\) 表现得比奶牛 \(B\) 要好。

【输出】

输出一行,包含一致的奶牛的对数。

【输入样例】

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

【输出样例】

4

【核心思想】

  1. 问题分析:给定 \(K\) 次训练课中 \(N\) 头奶牛的排名,求一致奶牛对数——即存在一对奶牛 \((A, B)\),使得在全部 \(K\) 次排名中 \(A\) 都排在 \(B\) 前面(\(A\) 始终优于 \(B\))。本质是一个多轮偏序关系的交集统计问题。

  2. 算法选择

    • 邻接矩阵标记法:用 \(N \times N\) 的二维数组 \(a[i][j]\) 记录"奶牛 \(i\) 是否在某次训练中优于奶牛 \(j\) 的反面"(即 \(j\) 优于 \(i\)),最终统计从未出现反例的奶牛对
    • 关键观察:若奶牛 \(i\) 在全部 \(K\) 次中都优于奶牛 \(j\),则 \(a[j][i]\) 在所有 \(K\) 轮中都不会被标记为 \(1\)
  3. 关键步骤

    • 初始化:读取 \(K\)(训练次数)和 \(N\)(奶牛数量),初始化标记矩阵 \(a[1..N][1..N] = 0\)
    • 逐轮标记(循环 \(K\) 次):
      • 读取当前轮次的排名数组 \(t[1..N]\)
      • 对于排名中的每对 \((t_i, t_j)\) 其中 \(i < j\)\(t_i\) 优于 \(t_j\)):
        • 标记 \(a[t_j][t_i] = 1\)(表示存在一次训练使得 \(t_j\) 排在 \(t_i\) 后面,即 \(t_i\) 优于 \(t_j\) 的"反例"已被记录)
    • 对角线处理:将 \(a[i][i] = 1\)(自身不构成有效对)
    • 统计答案:遍历整个矩阵,\(a[i][j] = 0\) 表示奶牛 \(i\) 在全部 \(K\) 次中从未被 \(j\) 超越(即 \(i\) 始终优于 \(j\)),\(\text{ans}++\)
    • 输出答案\(\text{ans}\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(K \cdot N^2 + N^2) = O(K \cdot N^2)\),每轮排名需要 \(O(N^2)\) 标记所有对,最后 \(O(N^2)\) 统计
    • 空间复杂度:\(O(N^2)\),邻接矩阵存储
  5. 邻接矩阵统计偏序关系的核心思想

    • 反例标记策略:不直接标记"谁优于谁",而是标记"谁曾经劣于谁"。若 \(a[j][i] = 1\),说明至少有一次训练中 \(i\) 优于 \(j\) 不成立(即 \(j\) 排在 \(i\) 前面)
    • 全零即一致\(a[i][j] = 0\) 意味着在所有 \(K\) 轮中,\(i\) 从未被 \(j\) 超越,即 \(i\) 始终优于 \(j\),构成一致对
    • 矩阵对称性利用:只需关注上三角或下三角,但代码中遍历全矩阵,对角线置 \(1\) 排除自身对
    • 多轮交集的简洁实现:通过累次标记反例,最终矩阵中的 \(0\) 即为多轮偏序关系的交集
    • 适用于多轮排名一致性判定、偏序关系统计、竞赛图分析类问题

【解题思路】

【算法标签】

入门 #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int k, n, a[25][25]={0}, t[25]={0}, ans=0;
int main()
{
    cin >> k >> n;  // 输入k和n
    while (k--) {  // 依次读入k行数据
        for (int i=1; i<=n; i++) {  // 依次记录n个数据
            cin >> t[i];
        }
        for (int i=1; i<n; i++) {  // 定义二维矩阵,分别记录每2个数的先后关系
            for (int j=i+1; j<=n; j++) {
                a[t[j]][t[i]] = 1;  // t[j]在t[i]后面,将表现不好的标记为1
            }
        }
    }
    for (int i=1; i<=n; i++) {  // 处理二维矩阵的斜线位置(左上到右下的斜线)
        for (int j=1; j<=n; j++) {
            if (i==j) a[i][j] = 1;  // 这些也标记为1
        }
    }
    for (int i=1; i<=n; i++) {  // 遍历二维矩阵
        for (int j=1; j<=n; j++) {
            if (a[i][j]==0) ans++;  // 对于值为0的单元格,就是一对A优于B的存在。统计ans就是一致的奶牛的对数
        }
    }
    cout << ans << endl;
    return 0;
}

【运行结果】

3 4
4 1 2 3
4 1 3 2
4 2 1 3
4
posted @ 2026-08-29 09:38  团爸讲算法  阅读(6)  评论(0)    收藏  举报