问题描述
设有 n = 2 k n=2^k n=2k个运动员要进行羽毛球循环赛,现要设计一个满足以下要求的比赛日程表:
- 每个选手必须与其它n-1个选手各赛一次;
- 每个选手一天只能比赛一次;
- 循环赛一共需要进行n-1天。由于n=2k,显然n为偶数。
代码
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int[][] schedule;
public static void process(int k, int[][] schedule) {
// 计算运动员总人数
int n = 1;
for (int i = 0; i < k; i++) n*=2;
// 分配空间,零行零列空缺
schedule = new int[n+1][n+1];
// 顺次列出运动员编号schedule[1][1]~schedule[1][n]
for (int i = 1; i <= n; i++) schedule[1][i] = i;
int m = 1;
for (int s = 1; s <= k; s++) {
n/=2;
for (int t = 1; t <= n; t++) {
for (int i = m + 1; i <= 2 * m; i++) {
for (int j = m + 1; j <= 2 * m; j++) {//
// 根据这个日程表对应规律可知,行和列都差了m
schedule[i][j+(t-1)*m*2]=schedule[i-m][j+(t-1)*m*2-m];
schedule[i][j+(t-1)*m*2-m]=schedule[i-m][j+(t-1)*m*2];
}
}
}
m*=2;
}
for (int[] item : schedule)
System.out.println(Arrays.toString(item));
System.out.println();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//输入k,运动员的人数为2^k
int k;
Scanner scanner = new Scanner(System.in);
k = scanner.nextInt();
process(k,schedule);
}
}
结果展示
输入为3,即运动员数量为8个
代码思路
当运动员人数为8时,我们可以很容易排出一张日程表,满足题目的要求。
观察后发现日程表第一行即运动员编号的顺序排列,然后我们发现第二行与第一行非常大的关系,即交叉复制的关系。
而第三四行似乎也与前两行有交叉复制的关系,
这样看来,我们只要不断地交叉复制前面地数据即可,那么在代码中如何体现这个思路呢
我把代码中最重要的部分重新拿出来了,我们一起看一下程序是如何运行的。
int m = 1;
for (int s = 1; s <= k; s++) {//第一个循环
n/=2;
for (int t = 1; t <= n; t++) {//第二个循环
for (int i = m + 1; i <= 2 * m; i++) {//第三个循环
for (int j = m + 1; j <= 2 * m; j++) {//第四个循环
// 根据这个日程表对应规律可知,行和列都差了m
schedule[i][j+(t-1)*m*2]=schedule[i-m][j+(t-1)*m*2-m];
schedule[i][j+(t-1)*m*2-m]=schedule[i-m][j+(t-1)*m*2];
}
}
}
m*=2;
}
由于这个规模是以除2的速率缩小的的,即要解决一个8人的日程表,第一次完成第二行,第二次完成三四行,第三次就完成了日程表,因此我们的第一个循环用k约束复制的轮数。需要记住的是,第s次循环我们要完成 2 i − 1 2^{i-1} 2i−1行(1行,2行,4行,…)。
而这里的n又有什么作用呢?
我们观察到,第一次需要交叉复制4次,第二次2次,第三次1次,不明白的可以参看上面的图片,因此我们用n来约束交叉复制的次数。这样我们就知道了第三四个循环其实是完成一次交叉复制。由变量i来控制行,j来控制列。
第二行交叉复制的次序
第三四行交叉复制的次序