问题描述:
设有n=2k(2的k次方)个选手要进行网球循环赛,要求设计一个满足以下要求的比赛日程表:
(1)每个选手必须与其他n-1个选手各赛一次;(2)每个选手一天只能赛一次。
按此要求,可将比赛日程表设计成一个 n 行n-1列的二维表,其中,第 i 行第 j 列表示和第 i 个选手在第 j 天比赛的选手。
循环赛日程表问题主要使用分治思想,将问题分成2个子问题,子问题再分成子问题,然后分别解决子问题合并子问题的解
递归求解:
首先初始化第一列。将要解决的问题折半,分成上半部分和下半部分,然后分别求2个子问题的解,依次类推,直到剩余2个队伍,此时在2X2的方格内,交换两支队伍,这样2x2方格部分已解决,然后在4x4内交换,依次类推合并各个子问题的解。
以下图为例n=8分成2个n=4子问题,再分成4个n=2的子问题,然后执行交换函数
递归求解的方向是上下
非递归求解:
首先初始化第一列。然后从左上角开始,以2为单位交换2x2方格队伍,4x4方格队伍以此类推
非递归求解的方向是从左上角斜着往下
以下是代码:
///**********循环赛日程表问题****************///
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
int a[100][100]; ///日程表,储存最后的结果
void swap(int x,int y,int size){ ///交换对角的两个区域
int s = size/2;
for(int i = x + s; i <= x+size-1; ++i){
for(int j = 0; j < s; ++j){
a[i][y+s+j] = a[i-s][y+j];
}
}
for(int i = x; i <= x+s-1; ++i)
for(int j = 0; j < s; ++j) a[i][y+s+j] = a[i+s][y+j];
}
void solve1(int x,int y,int size){ ///递归解决
if(size==2){ ///边界
swap(x,y,2);
return;
}
solve1(x,y,size/2); ///递归解决上半部分
solve1(x+size/2,y,size/2); ///递归解决下半部分
swap(x,y,size); ///合并结果
}
void solve2(int n){ ///非递归解决
for(int i = 2; i <= n; i*=2){
for(int j = 1; j <= n; j+=i){
swap(j,1,i);
}
}
}
void print(int n){ ///打印结果
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= n; ++j) printf("%d ",a[i][j]);
printf("\n");
}
}
int main()
{
int n;
scanf("%d",&n); ///n只队伍
for(int i = 1; i <= n; ++i) a[i][1] = i;///初始化第一列
solve1(1,1,n); ///递归
print(n);
printf("------------------------------\n");
solve2(n); ///非递归
print(n);
return 0;
}