N 頭牛(N ≤ 100),M 場比賽結果(M ≤ 4500),每場 A B 表示 A 勝 B。技能值唯一且固定,故勝負關係具有傳遞性:A 勝 B 且 B 勝 C ⇒ A 勝 C。求能確定確切排名的牛數(即與其他所有牛勝負關係均已知)。
建立鄰接矩陣 win[i][j] 表示 i 是否勝 j(直接或間接)。傳遞閉包計算後,牛 i 確定排名 ⇔ ∀j≠i, win[i][j] || win[j][i] 成立(即與其餘 N-1 頭牛均有已知勝負關係)。
三重循環計算傳遞閉包:
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] = true;
時間複雜度 O(N³) = 10⁶,N=100 完全可接受。
#include
using namespace std;
int main() {
int n, m;
while (scanf("%d%d", &n, &m) == 2) {
vector> win(n+1, vector(n+1, false));
for (int i = 0; i < m; ++i) {
int a, b; scanf("%d%d", &a, &b);
win[a][b] = true; // a 勝 b
}
// Floyd-Warshall 傳遞閉包
for (int k = 1; k <= n; ++k)
for (int i = 1; i <= n; ++i)
if (win[i][k]) // 微優化:i 能到 k 才內層循環
for (int j = 1; j <= n; ++j)
if (win[k][j]) win[i][j] = true;
int ans = 0;
for (int i = 1; i <= n; ++i) {
int known = 0;
for (int j = 1; j <= n; ++j)
if (i != j && (win[i][j] || win[j][i])) ++known;
if (known == n - 1) ++ans;
}
printf("%d\n", ans);
}
return 0;
}
輸入:
5 5 4 3 4 2 3 2 1 2 2 5
勝負關係:
傳遞閉包後:
輸出:2(牛 2 和 5)✓
boolbitset<101> 存儲每行,內層循環變為 win[i] |= win[k],利用 CPU 位並行加速 64 倍if (win[i][k]) 守衛內層循環while(scanf(...)==2) 讀取POJ 3660 是 Floyd-Warshall 算法的直接應用題。核心在於建立正確的有向圖模型,理解「確定排名」等價於「與所有其他節點存在已知勝負關係」。對於小規模節點(N ≤ 200)的傳遞閉包問題,Floyd 算法是性價比最高的選擇。