1. 程式人生 > >NOIP2011普及組第3題 瑞士輪

NOIP2011普及組第3題 瑞士輪

題目描述

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的選手的實力值。

【資料範圍】 
對於 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

輸出

輸出只有一行,包含一個整數,即R輪比賽結束後,排名第Q的選手的編號。

樣例輸入

2 4 2
7 6 6 7
10 5 20 15

樣例輸出

1

解析:

這題可以考慮排序,第一輪直接排序,求出結果。接著我們注意到,每一輪必定會產生n個勝利者得分,n個失敗者不得分,這兩組各自內部的排名是不變的。我們可以考慮把他們分開處理。則只需要合併的操作。時間複雜度為O(nlogn+n*r)。

1、定義結構體儲存資料,包括選手的編號、分數、實力;

2、首先用sort()函式對資料進行一次快排,按照分數從大到小,如果分數相同,編號小的在前;

3、參照題意進行模擬,每輪比賽後需要根據分數進行重新排序
① 若是用sort()函式進行快排,TLE超時;
②對資料進行分析,每輪比賽後,可將勝利者分為一組,負者分為一組;而這兩組資料本身是排好序的(從大到小),因此考慮使用歸
並排序;

4、最後的題解可以理解為 模擬+歸併

AC碼:

#include<iostream>
#include<algorithm>
using namespace std;
int n,r,q;
struct node{
	int w;//實力值 
	int s;//代表得分 
	int ni;//編號 
}wh[200001],win[200001],fa[200001];//存當前得分,勝利者,失敗者   
bool cmp(node a,node b)
{
	if(a.s  ==b.s )
	return a.ni <b.ni ;//如果得分相同,編號小的放在前面 
	return a.s >b.s ;//否則得分多的放前面 
 } 
void pp(int n,int r)
{//n名選手,r輪比賽 
	int i,j;
	for(j=1;j<=r;j++)
	{
		int wi=1,f=1;
		for(i=1;i<=n;i+=2)
		{
			if(wh[i].w >wh[i+1].w )
			{//第i個人勝利 
				win[wi]=wh[i];//記錄勝利者
				wi++;
				fa[f]=wh[i+1];//記錄失敗者 
				f++;
			}
	    	if(wh[i].w <wh[i+1].w )
			{//第i人失敗 
				win[wi]=wh[i+1];//記錄勝利者 
				wi++;
				fa[f]=wh[i];//記錄失敗者
				f++;
			}
		}
		for(i=1;i<=n/2;i++)
		win[i].s ++;//勝利者 得分+1
		int x=1,y=1;
		i=1;
		while(x<=n/2&&y<=n/2)
		{//將當前一輪結束的得分存入wh[]中 
		//總分多的先入陣列, 
			if(win[x].s>fa[y].s )
			wh[i++]=win[x++];
			if(win[x].s<fa[y].s )
			wh[i++]=fa[y++];
			//如果總分相同,編號小的先入陣列 
			if(win[x].s ==fa[y].s&&win[x].ni <fa[y].ni )
			wh[i++]=win[x++];
			if(win[x].s ==fa[y].s&&win[x].ni>fa[y].ni )
			wh[i++]=fa[y++];
		}
		if(x>n/2)//勝利的存入完後,把剩餘的失敗的接著存入 
		while(y<=n/2)
		wh[i++]=fa[y++];
		else//失敗的存入完後,把剩餘的勝利的接著存入
		while(x<=n/2)
		wh[i++]=win[x++];
	}
}
int main()
{
	scanf("%d %d %d",&n,&r,&q);
	n*=2;
	int i,j,k;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&wh[i].s );//輸入初始分 
		wh[i].ni =i;//將其編號存入ni中 
	}
	for(i=1;i<=n;i++)
	scanf("%d",&wh[i].w );//輸入能力值 
	sort(wh+1,wh+n+1,cmp);//按照要求排序 
	pp(n,r);//呼叫work()函式,進行r場比賽,得到最終排名 
	printf("%d\n",wh[q].ni );//輸出第q的編號
	return 0;
}