OJ地址:
https://www.luogu.org/problemnew/show/P1309
http://bailian.openjudge.cn/practice/4031/
-
总时间限制: 2000ms 单个测试点时间限制: 1000ms 内存限制: 65535kB
-
描述
-
【背景】
在双人对决的竞技性比赛,如乒乓球、羽毛球、国际象棋中,最常见的赛制是淘汰赛和循环赛。前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高。后者的特点是较为公平,偶然性较低,但比赛过程往往十分冗长。
本题中介绍的瑞士轮赛制,因最早使用于 1895 年在瑞士举办的国际象棋比赛而得名。它可以看作是淘汰赛与循环赛的折衷,既保证了比赛的稳定性,又能使赛程不至于过长。
【问题描述】
2*N名编号为 1~2N的选手共进行 R轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。 选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。 每轮比赛的对阵安排与该轮比赛开始前的排名有关:第 1 名和第 2 名、第 3 名和第 4 名、……、第 2K – 1 名和第 2K名、…… 、第 2N – 1 名和第2N名,各进行一场比赛。每场比赛胜者得 1 分,负者得 0 分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。
现给定每个选手的初始分数及其实力值,试计算在 R 轮比赛过后,排名第 Q 的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。
输入
输入的第一行是三个正整数 N、R、Q,每两个数之间用一个空格隔开,表示有 2*N 名选手、R 轮比赛,以及我们关心的名次 Q。
第二行是 2*N个非负整数 s1, s2, …, s2N,每两个数之间用一个空格隔开,其中 si 表示编号为 i 的选手的初始分数。
第三行是 2*N个正整数 w1, w2, …, w2N,每两个数之间用一个空格隔开,其中 wi 表示编号为 i 的选手的实力值。
输出
输出只有一行,包含一个整数,即 R 轮比赛结束后,排名第 Q 的选手的编号。
样例输入
2 4 2 7 6 6 7 10 5 20 15
样例输出
1
提示
对于 30%的数据,1 ≤ N ≤ 100;
对于 50%的数据,1 ≤ N ≤ 10,000;
对于 100%的数据, 1 ≤ N ≤ 100,000, 1 ≤ R ≤ 50, 1 ≤ Q ≤ 2N, 0 ≤ s1, s2, …, s2N ≤ 108, 1 ≤ w1, w2, …, w2N ≤ 108。
算法分析:
参考https://blog.csdn.net/dengping_ss/article/details/49833557
对于这道题,最容易想到的算法恐怕就是直接模拟比赛过程,每轮比赛前都用快排排一次,然后再根据选手的实力值决定胜负。但是这样算下来,时间复杂度达到了O(R*(N*logN+N)),但实际动作的次数还要加一个常数倍,因为一共有2*N个人。
把数据范围看一下,就知道这种算法肯定是要超时的,因此只要还有时间,应该尝试更为高效的算法。
很容易可以知道,每轮比赛结束后,胜利者和失败者两个群体中内部的顺序是不会被打乱的。也就是说,第一轮比赛后每一对人中的胜利者拿来排序之后跟原来他们比赛前的顺序是一样的,当然失败者的一群人也一样。因此,很容易就想到之前那个算法为什么不够高效了,因为快排的效率高是针对随机的数列的,而这里每一轮比赛下来,有许多人(一半人)的相对顺序已经确定了。因此就想到了归并排序,这样,每一轮比赛后用O(N)的复杂度归并一次就好,而不用快排,那样会损失掉已有的信息。这样,估算下来,时间就够了。
值得注意的是:在第一轮比赛前,并不知道待比赛选手的排名,而这时又不可能得到任何已有的信息,因为本来直接给出的初始值s是无序的,所以这时应使用快排,而且要注意初始值相同的情况,但这里已经是细节,不是重点了。
另外,每一轮比赛过程中,两个人进行一场比赛后,这两人都应该分到两个临时数组(这两个数组分别代表胜利者群体和失败者群体)以便后续的归并操作。这个时候要特别注意:这一场比赛的两个人若是打平手了,那么两人都应该分到失败者群体,因为他们两人的积分都没有增加。
AC代码如下:
1 #include<stdio.h> 2 #include<stdlib.h> 3 struct person 4 { 5 int ID,s,w;//选手编号、分值、实力值 6 }; 7 int cmp(const void *a,const void *b) 8 { 9 struct person *x,*y; 10 x=(struct person*)a; 11 y=(struct person*)b; 12 if(x->s > y->s) return -1; 13 else if(x->s < y->s) return 1; 14 else 15 { 16 if(x->ID > y->ID) return 1; 17 else return 0; 18 } 19 } 20 int main() 21 { 22 int N,R,Q; 23 struct person *a=NULL,*b=NULL,*c=NULL; 24 int i,j,NN,kb,kc,ib,ic; 25 26 scanf("%d%d%d",&N,&R,&Q); 27 NN=2*N; 28 a=(struct person *)malloc(sizeof(struct person)*NN); 29 b=(struct person *)malloc(sizeof(struct person)*NN); 30 c=(struct person *)malloc(sizeof(struct person)*NN); 31 32 for(i=0;i<NN;i++) 33 { 34 a[i].ID=i+1; 35 scanf("%d",&a[i].s); 36 } 37 for(i=0;i<NN;i++) 38 scanf("%d",&a[i].w); 39 qsort(a,NN,sizeof(a[0]),cmp); 40 41 for(i=0;i<R;i++) 42 { 43 kb=kc=0; 44 for(j=0;j<NN;j=j+2)//模拟一轮比赛 45 { 46 if(a[j].w>a[j+1].w) { a[j].s++; b[kb++]=a[j]; c[kc++]=a[j+1]; } 47 else if(a[j].w < a[j+1].w) { a[j+1].s++; b[kb++]=a[j+1]; c[kc++]=a[j]; } 48 else 49 { 50 c[kc++]=a[j]; c[kc++]=a[j+1]; 51 } 52 } 53 54 ib=ic=0; 55 for(j=0;ib<kb||ic<kc;j++)//一轮比赛完成以后,做一次归并操作使得a[]按积分降序排序 56 { 57 if(ic>=kc || (ib<kb&&b[ib].s>c[ic].s)||(ib<kb&&b[ib].s==c[ic].s&&b[ib].ID<c[ic].ID)) a[j]=b[ib++]; 58 else a[j]=c[ic++]; 59 } 60 } 61 printf("%d\n",a[Q-1].ID); 62 free(a); 63 return 0; 64 }