本文主要代码及思路来源于:【算法设计与分析(第五版)】【王晓东】
【问题描述】设有n=2k个选手要进行网球循环赛,要求设计一个满足以下要求的比赛日程表:
(1)每个选手必须与其他n-1个选手各赛一次;
(2)每个选手一天只能赛一次;
(3)循环赛在n-1天之内结束。
【问题求解】按问题要求可将比赛日程表设计成一个n行n-1列的二维表,其中第i行、第j列表示和第i个选手在第j天比赛的选手。
假设n位选手被顺序编号为1、2、…、n(2k)。
将n=2k问题划分为4部分:
(1)左上角:左上角为2k-1个选手在前半程的比赛日程(k=1时直接给出,否则,上一轮求出的就是2k-1个选手的比赛日程)。
(2)左下角:左下角为另2k-1个选手在前半程的比赛日程,由左上角加2k-1得到,例如22个选手比赛,左下角由左上角直接加2(2k-1)得到,23个选手比赛,左下角由左上角直接加4(2k-1)得到。
(3)右上角:将左下角直接复制到右上角得到另2k-1个选手在后半程的比赛日程。
(4)右下角:将左上角直接复制到右下角得到2k-1个选手在后半程的比赛日程。
按分治策略,将所有的选手分为两半,n个选手的比赛日程表就可以通过为n/2个选手设计的比赛日程表来决定。递归地对选手进行分割,直到只剩下2个选手时,比赛日程表的制定就变得很简单。这时只要让这2个选手进行比赛就可以了。
完整代码
书上的代码暂时没看懂,挂一个好理解的
#include <stdio.h>
#define MAX 100+1 // 数组下标为0的单元不用
void Plan(int Table[][MAX], int k)
{
int i, j, n, t, temp;
n = 2; //n从2^1=2开始
Table[1][1] = 1;
Table[1][2] = 2; //求解2个选手比赛日程,得到左上角元素
Table[2][1] = 2;
Table[2][2] = 1;
for(t = 1; t < k; t++) { //迭代处理,依次处理2^2(t=1)…,2^k(t=k-1)个选手
temp = n; //temp=2^t
n = n * 2; //n=2^(t+1)
for(i = temp + 1; i <= n; i++) //填左下角元素
for(j = 1; j <= temp; j++)
Table[i][j] = Table[i - temp][j] + temp; //左下角元素和左上角元素的对应关系
for(i = 1; i <= temp; i++) //填右上角元素
for(j = temp + 1; j <= n; j++)
//Table[i][j] = Table[i + temp][(j + temp) % n];
Table[i][j] = Table[i + temp][j - temp];
for(i = temp + 1; i <= n; i++) //填右下角元素
for(j = temp + 1; j <= n; j++)
Table[i][j] = Table[i - temp][j - temp];
}
}
int main()
{
int Table[MAX][MAX]; //存放比赛日程表(行列下标为0的元素不用)
int k = 5;
int n = 1 << k; //n等于2的k次方即n=2^k
Plan(Table, k); //产生n个选手的比赛日程表
for(int i = 1; i <= n; i++) { //输出比赛日程表
for(int j = 1; j <= n; j++)
printf("%4d", Table[i][j]);
printf("\n");
}
}