一头牛的名次能确定,当且仅当它和其余每一头牛的强弱关系都已知(直接或间接)。
样例答案:牛 2、牛 5 共 2 头。
GESP8 冲刺课堂页:把“谁一定打得过谁”做成一张表 —— 传递闭包(Floyd 框架)。
一头牛的名次能确定,当且仅当它和其余每一头牛的强弱关系都已知(直接或间接)。
样例答案:牛 2、牛 5 共 2 头。
① 把胜负关系填进 win[i][j] 表 → ② 用 Floyd 框架补全间接关系 → ③ 数每头牛已知几对 → ④ 满 4 对的就确定名次。
win[i][j]=1 表示“i 一定比 j 强”。胜负有传递性:A 胜 B、B 胜 C ⟹ A 胜 C,所以可以用传递闭包把所有能推出的关系补齐。
和 Floyd 最短路同一个三层循环框架,这里求的是“可达 / 强弱”,不是距离。
牛 i 名次确定 ⟺ 对所有 j≠i,win[i][j] 或 win[j][i] 至少一个为 1。
也就是:它和其余 n-1 头每一对都分出了胜负——赢了多少、输了多少都清楚,名次自然唯一。
第 0 步
一头牛“名次确定”靠什么判断?
win[i][j] |= win[i][k] && win[k][j] 在算什么?
样例里牛 5 为什么名次能确定?
| 牛 | 赢过(含间接) | 输给(含间接) | 已知对数 | 名次确定? |
|---|---|---|---|---|
| 1 | 2, 5 | — | 2 / 4 | 否 |
| 2 | 5 | 1, 3, 4 | 4 / 4 | 是 ✓(第 4 名) |
| 3 | 2, 5 | 4 | 3 / 4 | 否 |
| 4 | 2, 3, 5 | — | 3 / 4 | 否 |
| 5 | — | 1, 2, 3, 4 | 4 / 4 | 是 ✓(第 5 名) |
只有“赢过 + 输给”加起来正好 4 头的牛,名次才唯一 → 答案 = 2。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<vector<int>> win(n + 1, vector<int>(n + 1, 0));
for (int e = 0; e < m; e++) {
int a, b;
cin >> a >> b;
win[a][b] = 1; // a yiding sheng b
}
// Floyd kuangjia: chuandi bibao
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (win[i][k] && win[k][j]) win[i][j] = 1;
int ans = 0;
for (int i = 1; i <= n; i++) {
bool ok = true;
for (int j = 1; j <= n; j++) {
if (i == j) continue;
if (!win[i][j] && !win[j][i]) { ok = false; break; }
}
if (ok) ans++;
}
cout << ans << '\n';
return 0;
}
k 必须在最外层,这是 Floyd 框架的灵魂。|= 的思想:只把 0 改成 1,绝不把已有的 1 覆盖回去(写 if(...) win[i][j]=1; 最稳)。win[i][j] 和 win[j][i] 任一为 1 就算“已知”,别只数赢的。当 N 更大时,可以用 bitset 把每一行压成位向量,传递闭包的内层循环交给 CPU 一次处理 64 位:
本题 N≤100,普通三层循环已足够;bitset 是“同一个传递闭包,换更快的实现”,留作进阶练习。