题目描述
一场欧洲足球锦标赛有 nnn 支参赛队伍,且 nnn 是 222 的幂。比赛采用淘汰赛制:
- 第一轮进行 n/2n/2n/2 场比赛,每队与另一队比赛;
- 每轮结束后,只有获胜的队伍晋级下一轮;
- 第二轮进行 n/4n/4n/4 场比赛,依此类推,直到决出冠军。
你最喜欢的队伍是 111 号队(荷兰队)。已知:
- 队伍 111 至少能打败其他队伍中的一半;
- 对于每一支队伍 111 无法直接打败的队伍 ttt,都存在另一支队伍 t′t't′,t′t't′ 能打败 ttt,且 t′t't′ 被队伍 111 打败。
你已知每两支队伍之间的胜负关系(用一个 n×nn \times nn×n 的二进制矩阵表示)。请构造一个赛程安排,使得队伍 111 能够成为冠军。
输入格式
- 第一行:队伍数 nnn(2≤n≤10242 \le n \le 10242≤n≤1024,且 nnn 是 222 的幂)
- 接下来 nnn 行:每行一个长度为 nnn 的字符串,表示该队伍对其他队伍的胜负关系(
1表示胜,0表示负,对角线为0)
输出格式
输出 n−1n - 1n−1 行,每行两个整数 x yx\ yx y,表示 xxx 与 yyy 进行比赛。赛程必须保证队伍 111 获胜。
题目分析
关键性质
设:
- 黑色队伍:队伍 111 无法打败的队伍;
- 灰色队伍:队伍 111 能打败,且能打败至少一个黑色队伍的队伍;
- 白色队伍:队伍 111 能打败,但不能打败任何黑色队伍的队伍。
根据题目条件:
- 队伍 111 至少能打败一半的队伍,因此黑色队伍不超过 n/2−1n/2 - 1n/2−1(不包括自己);
- 对每个黑色队伍 ttt,存在一个灰色队伍 t′t't′ 能打败 ttt,且 t′t't′ 被队伍 111 打败。
构造思路
我们可以将每一轮的比赛安排分为四个阶段:
- 消灭黑色:用灰色或白色队伍去淘汰黑色队伍;
- 安排队伍 111:让队伍 111 与一个它能打败的队伍比赛;
- 黑色自相残杀:若还有未被淘汰的黑色队伍,让它们互相淘汰;
- 自由配对:剩下的队伍任意配对。
这样能确保:
- 所有黑色队伍在遇到队伍 111 之前就被淘汰;
- 队伍 111 在整个比赛中只遇到它能打败的对手;
- 最终队伍 111 夺冠。
解题步骤
- 读入 nnn 和胜负矩阵;
- 对当前轮次的队伍进行分类:黑色、灰色、白色;
- 按四阶段策略安排比赛:
- 阶段一:用灰色/白色淘汰黑色;
- 阶段二:安排队伍 111 的比赛;
- 阶段三:黑色队伍互相淘汰;
- 阶段四:剩余队伍自由配对;
- 递归处理下一轮晋级队伍,直到只剩一支队伍。
代码实现
// Foul Play
// UVa ID: 1609
// Verdict: Accepted
// Submission Date: 2025-11-23
// UVa Run Time: 0.140s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
vector<string> win;
int n;
vector<bool> used;
void scheduleRound(vector<int> teams) {
if (teams.size() == 1) return;
// 分类队伍
vector<int> black, gray, white;
for (int team : teams) {
if (team == 1) continue;
if (win[1][team] == '0') black.push_back(team);
else {
// 检查是否是灰色队伍(能打败某个黑色队伍)
bool isGray = false;
for (int b : black) {
if (win[team][b] == '1') {
isGray = true;
break;
}
}
if (isGray) gray.push_back(team);
else white.push_back(team);
}
}
vector<int> nextRound;
used.assign(n + 1, false);
// 阶段1:用灰色队伍消灭黑色队伍
for (int b : black) {
if (used[b]) continue;
bool eliminated = false;
for (int& g : gray) {
if (!used[g] && win[g][b] == '1') {
cout << g << " " << b << endl;
used[g] = used[b] = true;
nextRound.push_back(g);
eliminated = true;
break;
}
}
// 如果找不到灰色队伍,用白色队伍
if (!eliminated) {
for (int& w : white) {
if (!used[w] && win[w][b] == '1') {
cout << w << " " << b << endl;
used[w] = used[b] = true;
nextRound.push_back(w);
eliminated = true;
break;
}
}
}
}
// 阶段2:安排队伍1的比赛
if (!used[1]) {
for (int team : teams) {
if (team != 1 && !used[team] && win[1][team] == '1') {
cout << "1 " << team << endl;
used[1] = used[team] = true;
nextRound.push_back(1);
break;
}
}
}
// 阶段3:剩下的黑色队伍自相残杀
vector<int> remainingBlack;
for (int b : black) if (!used[b]) remainingBlack.push_back(b);
for (int i = 0; i < remainingBlack.size(); i += 2) {
if (i + 1 < remainingBlack.size()) {
int a = remainingBlack[i], b = remainingBlack[i + 1];
cout << a << " " << b << endl;
used[a] = used[b] = true;
if (win[a][b] == '1') nextRound.push_back(a);
else nextRound.push_back(b);
}
}
// 阶段4:剩下的队伍任意配对
vector<int> remaining;
for (int team : teams) if (!used[team]) remaining.push_back(team);
for (int i = 0; i < remaining.size(); i += 2) {
if (i + 1 < remaining.size()) {
int a = remaining[i], b = remaining[i + 1];
cout << a << " " << b << endl;
if (win[a][b] == '1') nextRound.push_back(a);
else nextRound.push_back(b);
}
}
scheduleRound(nextRound);
}
int main() {
while (cin >> n) {
win.resize(n + 1);
used.resize(n + 1);
for (int i = 1; i <= n; i++) {
cin >> win[i];
win[i] = " " + win[i];
}
vector<int> teams(n);
for (int i = 0; i < n; i++) teams[i] = i + 1;
scheduleRound(teams);
}
return 0;
}
复杂度分析
- 时间复杂度:每轮处理 O(n2)O(n^2)O(n2),总轮数为 O(logn)O(\log n)O(logn),因此总复杂度为 O(n2logn)O(n^2 \log n)O(n2logn),在 n≤1024n \le 1024n≤1024 时完全可行。
- 空间复杂度:O(n2)O(n^2)O(n2) 用于存储胜负矩阵。
总结
本题是一道构造型题目,关键在于利用题目给出的两个性质,将队伍合理分类,并设计一个四阶段比赛安排策略,确保队伍 111 不会遇到它无法打败的对手。这种分类+阶段化处理的方法在构造题中非常实用,值得掌握。

303

被折叠的 条评论
为什么被折叠?



