OJ地址:c++
https://www.luogu.org/problemnew/show/P1309算法
http://bailian.openjudge.cn/practice/4031/數組
【背景】
在雙人對決的競技性比賽,如乒乓球、羽毛球、國際象棋中,最多見的賽制是淘汰賽和循環賽。前者的特色是比賽場數少,每場都緊張刺激,但偶然性較高。後者的特色是較爲公平,偶然性較低,但比勝過程每每十分冗長。
本題中介紹的瑞士輪賽制,因最先使用於 1895 年在瑞士舉辦的國際象棋比賽而得名。它能夠看做是淘汰賽與循環賽的折衷,既保證了比賽的穩定性,又能使賽程不至於過長。
【問題描述】
2*N名編號爲 1~2N的選手共進行 R輪比賽。每輪比賽開始前,以及全部比賽結束後,都會按照總分從高到低對選手進行一次排名。 選手的總分爲第一輪開始前的初始分數加上已參加過的全部比賽的得分和。總分相同的,約定編號較小的選手排名靠前。 每輪比賽的對陣安排與該輪比賽開始前的排名有關:第 1 名和第 2 名、第 3 名和第 4 名、……、第 2K – 1 名和第 2K名、…… 、第 2N – 1 名和第2N名,各進行一場比賽。每場比賽勝者得 1 分,負者得 0 分。也就是說除了首輪之外,其它輪比賽的安排均不能事先肯定,而是要取決於選手在以前比賽中的表現。
現給定每一個選手的初始分數及其實力值,試計算在 R 輪比勝過後,排名第 Q 的選手編號是多少。咱們假設選手的實力值兩兩不一樣,且每場比賽中實力值較高的總能獲勝。測試
輸入spa
輸入的第一行是三個正整數 N、R、Q,每兩個數之間用一個空格隔開,表示有 2*N 名選手、R 輪比賽,以及咱們關心的名次 Q。
第二行是 2*N個非負整數 s1, s2, …, s2N,每兩個數之間用一個空格隔開,其中 si 表示編號爲 i 的選手的初始分數。
第三行是 2*N個正整數 w1, w2, …, w2N,每兩個數之間用一個空格隔開,其中 wi 表示編號爲 i 的選手的實力值。.net
輸出code
輸出只有一行,包含一個整數,即 R 輪比賽結束後,排名第 Q 的選手的編號。blog
樣例輸入排序
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 }