问题描述
有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;
}