|
|
|
|
## [$AcWing$ $447$. 瑞士轮](https://www.acwing.com/problem/content/449/)
|
|
|
|
|
|
|
|
|
|
### 算法1
|
|
|
|
|
(暴力枚举) $O(NRlogN)$直接按照题意编,每次都排序
|
|
|
|
|
|
|
|
|
|
**时间复杂度**
|
|
|
|
|
排序是瓶颈,$NRlogN$显然超时
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 算法2
|
|
|
|
|
(暴力枚举) $O(nlogn)$
|
|
|
|
|
此题中的排序是多关键字排序:总分较大者优先,编号较小者优先。
|
|
|
|
|
|
|
|
|
|
初始时先将所有参赛队员排序,这里可以采用快速排序,时间复杂度是 $O(nlogn)$。
|
|
|
|
|
|
|
|
|
|
接下来每轮比赛结束后,均需要将所有选手重新排序。
|
|
|
|
|
|
|
|
|
|
如果采用快速排序,则总时间复杂度是 $O(NRlogN)$,会超时;
|
|
|
|
|
可以发现每次比赛会将 $N$ 个同学的分数加$0$,另外 $N$ 个同学的分数加$1$,所有加$0$的 $N$ 个同学内部是有序的,加$1$的 $N$ 个同学内部也是有序的,因此这一步需要我们将两个有序序列合并,使用二路归并算法即可,每次归并的时间复杂度是 $O(N)$。
|
|
|
|
|
|
|
|
|
|
>**解释**:本题主要思路是多关键字排序和归并排序(快排和归并结合,并采用自定义)
|
|
|
|
|
如果只用快速排序或者归并排序,会超时
|
|
|
|
|
**思想**:每次赢得和输的人的排序是可以分组的,将他们分成一起,排好之后再用归并排序合并
|
|
|
|
|
**快速排序适合无序,归并排序适合有序**
|
|
|
|
|
|
|
|
|
|
#### 时间复杂度
|
|
|
|
|
排序是瓶颈,$nlogn$
|
|
|
|
|
|
|
|
|
|
### $Code$
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
const int N = 2e5 + 10;
|
|
|
|
|
struct Node {
|
|
|
|
|
int id; // 学生编号
|
|
|
|
|
int s; // 总分 score
|
|
|
|
|
int p; // 实力
|
|
|
|
|
} c[N], a[N], b[N]; // 结果数组,赢者数组,负者数组
|
|
|
|
|
|
|
|
|
|
bool cmp(Node &a, Node &b) {
|
|
|
|
|
if (a.s == b.s) return a.id < b.id;
|
|
|
|
|
return a.s > b.s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
// 加快读入
|
|
|
|
|
ios::sync_with_stdio(false), cin.tie(0);
|
|
|
|
|
|
|
|
|
|
int n, r, q; // 2*n名选手 ,r轮比赛,关心名次q
|
|
|
|
|
cin >> n >> r >> q;
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= 2 * n; i++) cin >> c[i].s, c[i].id = i; // 记录每个学生的初始分数,记录学生的id
|
|
|
|
|
for (int i = 1; i <= 2 * n; i++) cin >> c[i].p; // 记录学生的实力
|
|
|
|
|
sort(c + 1, c + 2 * n + 1, cmp); // 按初始分数由大到小排序,如果分数一样,那么序号小的在前
|
|
|
|
|
|
|
|
|
|
while (r--) { // 每一轮比赛
|
|
|
|
|
int al = 1, bl = 1;
|
|
|
|
|
for (int i = 2; i <= 2 * n; i += 2) {
|
|
|
|
|
if (c[i - 1].p > c[i].p) // 实力前面的大,那么前面的赢
|
|
|
|
|
// 前面的学生总分+1,前面学生进入a数组,后面学生进入b数组
|
|
|
|
|
c[i - 1].s++, a[al++] = c[i - 1], b[bl++] = c[i];
|
|
|
|
|
else // 否则后面的赢
|
|
|
|
|
// 后面学生分数+1,前面学生进入b数组,后面学生进入a数组
|
|
|
|
|
c[i].s++, a[al++] = c[i], b[bl++] = c[i - 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int i = 1, j = 1, k = 0;
|
|
|
|
|
while (i < al && j < bl) { // 扫描两个数组
|
|
|
|
|
if (cmp(a[i], b[j]))
|
|
|
|
|
c[++k] = a[i++]; // a先进入结果数组c,a中游标走到i
|
|
|
|
|
else
|
|
|
|
|
c[++k] = b[j++]; // b先进入结果数组c,b中游标走到j
|
|
|
|
|
}
|
|
|
|
|
// 将最后的遗留数组扫入结果数组
|
|
|
|
|
while (i < al) c[++k] = a[i++];
|
|
|
|
|
while (j < bl) c[++k] = b[j++];
|
|
|
|
|
}
|
|
|
|
|
// 输出排名第q位的学生序号
|
|
|
|
|
cout << c[q].id << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|