You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 447. 瑞士轮

算法1

(暴力枚举) O(NRlogN)直接按照题意编,每次都排序

时间复杂度 排序是瓶颈,NRlogN显然超时

算法2

(暴力枚举) O(nlogn) 此题中的排序是多关键字排序:总分较大者优先,编号较小者优先。

初始时先将所有参赛队员排序,这里可以采用快速排序,时间复杂度是 O(nlogn)

接下来每轮比赛结束后,均需要将所有选手重新排序。

如果采用快速排序,则总时间复杂度是 O(NRlogN),会超时; 可以发现每次比赛会将 N 个同学的分数加0,另外 N 个同学的分数加1,所有加0N 个同学内部是有序的,加1N 个同学内部也是有序的,因此这一步需要我们将两个有序序列合并,使用二路归并算法即可,每次归并的时间复杂度是 O(N)

解释:本题主要思路是多关键字排序和归并排序(快排和归并结合,并采用自定义) 如果只用快速排序或者归并排序,会超时 思想:每次赢得和输的人的排序是可以分组的,将他们分成一起,排好之后再用归并排序合并 快速排序适合无序,归并排序适合有序

时间复杂度

排序是瓶颈,nlogn

Code

#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;
}