原题链接:P1309 [NOIP2011 普及组] 瑞士轮 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意
排序,2N名选手,按排名每每两人比赛,不重叠,胜者得一分,再排名比赛,重复R轮,要求输出查询选手编号的排名,同分按编号小的在前,有绝对实力条件,即给定选手实力,高的胜
思路
两个问题:
- 如何排序
- 如何做好映射
排序问题,两两之间比赛,赢的加一分,输的不加,这样来看,每笔一轮都有可能产生新的排名,但是局部来看,在赢的人里,原本排名高的依旧高,在输的人里,原本低的依旧低
也就是说每轮赢的数组和输的数组都是有序的,只要做一次归并就可以
映射问题,用索引表示排名,用数值表示编号,维护一个数组记录编号,这个数组是归并排序的结果数组,高分在前,但其本身没有排序信息,也就是利用其他条件进行排序,可以维护一个分数数组,用这个数组编写cmp函数进行大小比较
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
int n, r, q;
int a[N];
int s[N];
int w[N];
//输赢数组
int W[N], L[N];
bool cmp(int x, int y)
{
if(s[x] == s[y]) return x < y;
return s[x] > s[y];
}
void merge(int W[], int L[])
{
int i = 1, j = 1, k = 1;
while(i <= n / 2 && j <= n / 2)
{
if(cmp(W[i], L[j]))
{
a[k ++] = W[i ++];
}
else
{
a[k ++] = L[j ++];
}
}
while(i <= n / 2) a[k ++] = W[i ++];
while(j <= n / 2) a[k ++] = L[j ++];
}
int main()
{
scanf("%d%d%d", &n, &r, &q);
n *= 2;
for(int i = 1; i <= n; i ++)
{
scanf("%d", &s[i]);
a[i] = i;
}
for(int i = 1; i <= n; i ++)
{
scanf("%d", &w[i]);
}
sort(a + 1, a + 1 + n, cmp);
while(r --)
{
//输、赢单独来看结果依旧是顺序的,不需要重新排序,但是输赢两队需要归并排序
for(int i = 1, j = 1; i <= n; i += 2, j ++)
{
if(w[a[i]] > w[a[i + 1]])
{
s[a[i]] ++;
W[j] = a[i];
L[j] = a[i + 1];
}
else
{
s[a[i + 1]] ++;
W[j] = a[i + 1];
L[j] = a[i];
}
}
merge(W, L);
}
printf("%d\n", a[q]);
return 0;
}