[归并排序]【洛谷p1039】瑞士轮 的详解

本蒟蒻做瑞士轮,结果发现了点小技巧,在看瑞士轮之前不妨拿来说一下。

一、cmp函数的小技巧        

        大家都知道,一个数组sort之后各元素(虽然不是真正意义上的各元素)下标会发生改变。有些题目中在进行一些判断时会命令值相等时取下标小的(在输入中出现时间早的),这时候有两种选择,一种是用结构体存各个值,这里讲一下另一种:多开几个数组。

        a[]用来存序号,v[]来存值,当然如果有别的条件可以再开其它数组。我们让a数组按v[]从大到小的顺序排,传入两个参数x,y作为两个下标,然后返回v[x]>v[y]即可完成从大到小的排序。这样做的好处是不会打乱v数组,因为进行排序的是a数组,而想访问v也很容易,v[ a[ i ] ]即可

        举个例子,某题目(不一定是瑞士轮)要求将n个x从大到小排,输出最大的编号,当x相等时,按y排,y相等时按出现时间早的在前。这时便可如此操作

bool (int x,int y){
	if(v[x]==v[y]){
		if(s[x]==s[y])
			return xs[y];
	}
	return v[x]>v[y];
}

二、瑞士轮的详解

    【题目大意】2*n个选手,每人有初始分与能力值,有r轮比赛,每轮比赛令1 2名对战、3 4名对战、5 6名对战,以此类推。其中每局比赛能力值高者胜,胜者(初始)分值+1,每轮比赛后排名都有可能改变。

    【思路介绍】看到此题,最先想到的肯定是for(i~r),每轮sort一遍。但这样会t,我们不妨再好好想想这道题。这道题每轮后排名可能会变,但只可能是邻位之间变化,如果每轮都sort,无疑会造成巨大的时间浪费,而归并排序就非常适合这种小范围的换位。

确认算法之后再想想如何将其实现,该题归并的关键是保证记录名次的数组降序排列。我们想,在每一轮中,比赛开始前是逆序,结束后胜的人分数都+1,还是逆序,败的人分数不变,仍然是逆序。让胜者入win[],败者入lose[],这样每轮win和lose便仍是有序的,所以只需要开两个数组,一个记录胜者,一个记录败者,在每层for(每一轮比赛)结束时进行合并即可。

合并的复杂度为n(2*n)有r轮就是稳定的n(2*n*r)。

        【AC】代码及细节解释

1、win[0],lose[0],num[0]统统都是计数器(反正我下标是从1开始记的,闲着也是闲着)这样可以帮你省下多达3*4=12个字节的巨大空间!!!(滑稽)

2、注意一下,选手有n*2个,交了多次才发现

#include
#include
#include
using namespace std;

int const maxn=200111;
int num[maxn],v[maxn],ab[maxn],win[maxn],lose[maxn],n,r,q;

bool cmp(int x,int y){
	if(v[x]==v[y])
		return xv[y];
	}
	
void merge(){
	int i=1,j=1;
	num[0]=0; 
	while(i<=win[0]&&j<=lose[0]){
		if(cmp(win[i],lose[j]))
			num[++num[0]]=win[i++];
		else
			num[++num[0]]=lose[j++];
	}
		while(i<=win[0])
			num[++num[0]]=win[i++];
		while(j<=lose[0])
			num[++num[0]]=lose[j++];
}

int main(){
	cin>>n>>r>>q;
	n*=2; 
	for(int i=1;i<=n;i++)
		num[i]=i;
	for(int i=1;i<=n;i++)
		cin>>v[i];
	for(int i=1;i<=n;i++)
		cin>>ab[i]; 
	sort(num+1,num+1+n,cmp);
	for(int i=1;i<=r;i++){
		win[0]=lose[0]=0;
		for(int j=1;j<=n;j+=2){//两个选手作为一组进行比赛,所以要+=2
			if(ab[num[j]]>ab[num[j+1]]){  
            	v[num[j]]++;  
            	win[++win[0]]=num[j];  
           		lose[++lose[0]]=num[j+1];  
          }  
        else{  
            v[num[j+1]]++;  
            win[++win[0]]=num[j+1];  
            lose[++lose[0]]=num[j];  
          }
		}
		merge();
	}
	cout<

你可能感兴趣的:(noip,题解,时间优化,排序,算法)