题目描述
有 nnn 支队伍(编号从 111 到 nnn)每年参加编程竞赛,比赛结束后会按成绩优劣进行排名。已知去年的排名列表。今年,组委会希望降低比赛的竞争性,决定不发布完整的排名列表(因为排名靠后的队伍可能会灰心)。相反,他们会发布一个完整的配对列表,这些配对中的两支队伍在去年和今年的相对排名顺序发生了变化。
例如,如果去年队伍 131313 排在队伍 666 前面,但今年队伍 666 排在队伍 131313 前面,那么就会公布配对 (6,13)(6, 13)(6,13)。这样可以让队伍跟踪他们与特定对手的进展,但不会给他们一个清晰的总体排名情况。
当然,这不会阻止你的队伍尝试确定总体排名列表。给定去年的排名和所有相对排名顺序发生变化的队伍配对列表,尽可能重构出今年的排名情况。如果给出的数据与任何可能的今年排名列表不一致,你也应该检测到这种情况。
输入格式
- 第一行:一个正整数,表示测试用例的数量(最多 100100100 个)
- 每个测试用例包含:
- 一行一个整数 nnn (2≤n≤5002 \leq n \leq 5002≤n≤500):队伍数量
- 一行 nnn 个整数 tit_iti (1≤ti≤n1 \leq t_i \leq n1≤ti≤n):去年的排名,从最好的队伍到最差的队伍
- 一行一个整数 mmm (0≤m≤250000 \leq m \leq 250000≤m≤25000):相对排名顺序发生变化的配对数量
- mmm 行,每行两个整数 aia_iai 和 bib_ibi (1≤ai<bi≤n1 \leq a_i < b_i \leq n1≤ai<bi≤n):相对排名顺序发生变化的队伍配对
输出格式
每个测试用例输出:
- 一行 nnn 个整数:今年的排名,从最好到最差,如果某个位置无法确定,用
?代替。如果数据与任何可能的排名列表不一致,输出IMPOSSIBLE
题目分析
问题本质
这是一个典型的拓扑排序问题,但需要处理两种特殊情况:
- 不一致性检测:当约束关系存在环时,无法形成有效的排名
- 不确定性处理:当拓扑排序过程中有多个选择时,相应位置的结果不确定
关键观察
- 排名关系的传递性:如果 AAA 排在 BBB 前面,BBB 排在 CCC 前面,那么 AAA 必须排在 CCC 前面
- 约束关系的建立:
- 根据去年的排名,我们可以知道所有队伍之间的完整相对顺序
- 对于发生变化的配对,今年的顺序与去年相反
- 对于未发生变化的配对,今年的顺序与去年相同
解题思路
-
建立关系矩阵:
- 首先构建一个 n×nn \times nn×n 的矩阵
lastYearOrder,记录去年所有队伍之间的相对顺序 - 如果队伍 iii 去年排在队伍 jjj 前面,则
lastYearOrder[i][j] = true
- 首先构建一个 n×nn \times nn×n 的矩阵
-
更新今年关系:
- 初始化
thisYearOrder为去年的关系 - 对于每个变化的配对 (a,b)(a, b)(a,b),交换
thisYearOrder[a][b]和thisYearOrder[b][a]的值
- 初始化
-
构建有向图:
- 将每个队伍作为图的一个节点
- 如果
thisYearOrder[i][j] = true,则添加一条从 iii 到 jjj 的有向边,表示 iii 排在 jjj 前面
-
拓扑排序:
- 计算每个节点的入度
- 使用队列进行拓扑排序
- 在排序过程中:
- 如果队列中同时有多个节点可选,说明结果不确定
- 如果最终排序结果包含的节点数不等于 nnn,说明存在环,数据不一致
-
输出结果:
- 存在环 → 输出
IMPOSSIBLE - 排序过程中有多个选择 → 输出
? - 否则 → 输出确定的排名
- 存在环 → 输出
算法复杂度分析
- 时间复杂度:O(n2+m)O(n^2 + m)O(n2+m),其中 nnn 是队伍数量,mmm 是变化配对数量
- 空间复杂度:O(n2)O(n^2)O(n2),主要用于存储关系矩阵
代码实现
// Rankings
// UVa ID: 12263
// Verdict: Accepted
// Submission Date: 2025-11-29
// UVa Run Time: 0.020s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 505;
vector<int> topologicalSort(int n, vector<vector<int>>& graph, vector<int>& indegree) {
vector<int> result;
queue<int> q;
// 找到所有入度为0的节点
for (int i = 1; i <= n; i++) {
if (indegree[i] == 0) {
q.push(i);
}
}
bool multiple = false; // 是否有多个选择
while (!q.empty()) {
if (q.size() > 1) {
multiple = true; // 有多个节点可选,结果不确定
}
int current = q.front();
q.pop();
result.push_back(current);
// 更新相邻节点的入度
for (int neighbor : graph[current]) {
indegree[neighbor]--;
if (indegree[neighbor] == 0) {
q.push(neighbor);
}
}
}
// 如果结果数量不等于n,说明有环
if (result.size() != n) {
return vector<int>(); // 返回空向量表示不可能
}
// 如果有多个选择,标记为不确定
if (multiple) {
return vector<int>(n, -1); // 用-1表示不确定
}
return result;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int testCases;
cin >> testCases;
while (testCases--) {
int n;
cin >> n;
vector<int> lastYear(n);
for (int i = 0; i < n; i++) {
cin >> lastYear[i];
}
// 构建去年的排名关系矩阵
vector<vector<bool>> lastYearOrder(n + 1, vector<bool>(n + 1, false));
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
lastYearOrder[lastYear[i]][lastYear[j]] = true;
}
}
int m;
cin >> m;
// 初始化今年的关系矩阵(开始时与去年相同)
vector<vector<bool>> thisYearOrder = lastYearOrder;
// 处理变化的关系
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
// 交换a和b的关系
swap(thisYearOrder[a][b], thisYearOrder[b][a]);
}
// 构建图
vector<vector<int>> graph(n + 1);
vector<int> indegree(n + 1, 0);
// 根据今年的关系构建有向图
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i != j && thisYearOrder[i][j]) {
graph[i].push_back(j);
indegree[j]++;
}
}
}
// 进行拓扑排序
vector<int> result = topologicalSort(n, graph, indegree);
if (result.empty()) {
// 存在环,不可能
cout << "IMPOSSIBLE\n";
} else if (result[0] == -1) {
// 结果不确定
for (int i = 0; i < n; i++) {
if (i > 0) cout << " ";
cout << "?";
}
cout << "\n";
} else {
// 确定的结果
for (int i = 0; i < n; i++) {
if (i > 0) cout << " ";
cout << result[i];
}
cout << "\n";
}
}
return 0;
}
总结
本题通过将排名问题转化为图论中的拓扑排序问题,巧妙地解决了排名关系的约束和传递性问题。关键在于:
- 正确建立约束关系:利用去年的完整排名和变化配对构建今年的相对顺序
- 处理特殊情况:检测环的存在(不一致性)和拓扑排序的多解性(不确定性)
- 高效实现:使用邻接矩阵存储关系,队列实现拓扑排序
这种将实际问题抽象为图论模型的方法在竞赛编程中非常常见,掌握这种思维方式对于解决类似问题很有帮助。
1164

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



