网球循环赛日程表

问题描述

有n个运动员要进行网球循环赛。设计一个满足以下要求的比赛日程表:
1):每个选手必须与其他n-1个选手各赛一次;
2):每个选手每天只能赛一次;
3):当n是偶数时,循环赛进行n-1天。当n是奇数时,循环赛进行n天。

算法分析

(1)对于2^k规模的问题

对于这样的问题,我们可以直接从最小规模开始推导:
定义一张赛程表,第一列表示选手标号,后面的第i列表示第i天的选手赛程

  • k=2
    在这里插入图片描述
  • k=4
    在这里插入图片描述
  • k=8
    在这里插入图片描述

  • 很容易发现,这个过程其实就是反复执行了一个处理左下角,处理右上角,处理右下角的操作
  • 左下角的值等于当前位置的区域加上当前规模k/2
  • 右上角的值等于左下角的值
  • 右下角的值等于当前区域的值
  • 当规模为1时,当前值为1
  • 我们可以设计一个递归算法来处理:
void copy(int n)
{//复制
	int m = n/2;
	for(int i=1; i<=m; i++){
		for(int j=1; j<=m; j++){
			a[i][j+m] = a[i][j] + m;//左下角
			a[i+m][j] = a[i][j+m];//右上角
			a[i+m][j+m] = a[i][j];//右下角
		}
	}
}

void schedule (int n){
	if(n == 1){
		a[1][1] = 1;
		return;
	}
	schedule(n/2);
	copy(n);
}

考虑一般值

以上的算法可以很好地解决2^k规模的数据,但是对于更为一般的情况我们还需要做一些提升,策略如下

  • 当n=2k+1为奇数时,我们增加一个虚拟选手用来轮空
  • 当n=2k为偶数是,我们仍然可以用以上的算法来仅仅复制,因为整个赛程表是对称的可以直接进行复制处理
  • 递归处理时n/2如果为偶数,继续进行递归处理
  • 当n/2为奇数时,我们要进行修正

奇数时的修正

  • 对于一个选手a[i],如果他匹配的对手大于当前的规模m,说明它匹配的是一个虚拟选手
  • 我们将a[i]的虚拟选手修改为m+i
  • 同时修改a[m+i]号选手的对手为(b[i]+m)%n,以防止重复
  • 填充右上角之前,用一个数组b来记录当前a[i]的对手为a[m+i],a[i+m]的对手为a[i],在填充右上角时j-1相当于m,a[i][m+j]=b[i+j-1]
#include <iostream>
using namespace std;
int a[1001][1001];
int b[1001];
void copy1 (int n){//复制
	int m = n/2;
	for(int i=1; i<=m; i++){
		for(int j=1; j<=m; j++){
			a[i][j+m] = a[i][j] + m;//左下角
			a[i+m][j] = a[i][j+m];//右上角
			a[i+m][j+m] = a[i][j];//右下角
		}
	}
}
void copy2(int n){//奇数处理
	int m = n/2;
	for(int i=1; i<=m; i++){
        //按照递增构造赛程
		b[i] = m+i;
		b[m+i] = b[i];
	}
	//
	for(int i=1; i<=m; i++){
		for(int j=1; j<=m+1; j++){
			if(a[i][j]>m){//当a[i][j]>m的时候,认为这名选手的对手是虚拟选手
				a[i][j]=b[i];//由于a[i][j]轮空,我们直接让a[i][j]选手与m+i比赛保证与之前的构造一致
				a[m+i][j]=(b[i]+m)%n;//同时修改a[m+i]号选手的对手为(b[i]+m)%n,以防止重复
			}
			else{
			    //否则a[m+i]的对手构递增造为a[i][j]+m
				a[m+i][j]=a[i][j]+m;
			}
		}
		for(int j=2;j<=m;j++){
			a[i][m+j]=b[i+j-1];//右上角,此处的j-1相当于m
			a[b[i+j-1]][m+j]=i;//左下角,对称填充
		}
	}
}
void copy3(int n){
	if(n/2>1 && (n/2)%2!=0)
	copy2(n);
	else
	copy1(n);
}
void schedule (int n){
	if(n == 1){
		a[1][1] = 1;
		return;
	}
	if(n%2!=0) n++;
	schedule(n/2);
	copy3(n);
}
void output (int n){
	int m=n;
	bool flag=true;
	if(n%2!=0){
		flag=false;
		m++;
	}
	for(int i=1; i<=n; i++){
		for(int j=1; j<=m; j++){
			if(flag==false && a[i][j]==m)
			cout<<'x'<<" ";
			else
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
	}
}
int main(){
	int n;
	cin >> n;
	schedule(n);
	output(n);
	return 0;
}

在这里插入图片描述

原创文章 236 获赞 430 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_44307065/article/details/105156788