循环日程表问题
问题描述:
设有 n = 2^k 个运动员进行网球循环赛,需要设计一个满足以下要求的比赛日程表:
1.每个选手必须与其他 n-1 个选手各赛一次;
2.每个选手一天只能参赛一次;
3.循环赛一共进行 n-1 天;
按此要求设计一张比赛日程表,该表有 n 行和 n-1 列,第 i 行 j 列为第 i 个选手在第 j 天所遇到的选手。
算法分析:
循环日程表问题方法有很多种,分治是其中一种比较容易理解的方法.按分治策略,将所有的选手分为两半,则 n 个选手的比赛日程表可以通过n/2个选手的比赛日程表来决定。递归地用分治策略将选手进行划分,直到只剩下两个选手时,达到递归边界。这时仅有两个选手进行比赛。当k = 3时,则一共有8个选手进行比赛,先对赛程表的第一行进行1-8的赋值!根据第一行对第二行进行赋值:其中第一块为1号与2号的比赛日程,左上角与左下角的两小块分别为选手1第一天的比赛日程。据此,将左上角小块中的所有数字按其相对位置抄到右下角,又将左下角小块中的所有数字按其相对位置抄到右上角,这样我们就分别安排好了选手1至选手2在第一天的比赛日程.同理后面3块分别为:3与4,5与6,7与8的比赛日程。
根据第一二行对第三四行进行赋值:其中第一块为选手1至选手4的比赛日程,左上角与左下角的两小块分别为选手1,2号,选手3,4号第一天的比赛日程。据此,将左上角小块中的所有数字按其相对位置抄到右下角,又将左下角小块中的所有数字按其相对位置抄到右上角,这样我们就分别安排好了选手1至选手4在前3天的比赛日程.同理后面1块为:选手5至选手8在前3天的比赛日程。
根据前四行对后四行进行赋值:同中一块为选手1至选手8的比赛日程,左上角与左下角的两小块分别为选手1至选手4,选手5至选手6前3天的比赛日程。据此,将左上角小块中的所有数字按其相对位置抄到右下角,又将左下角小块中的所有数字按其相对位置抄到右上角,这样我们就分别安排好了选手1至选手8在前7天的比赛日程.依此思想容易将这个比赛日程表推广到具有任意多个选手的情形.
代码实现:
#include <stdio.h>
#include <iostream>
#include <math.h>
#include <string.h>
#include <algorithm>
using namespace std;
int maps[100][100];
void solve(int k,int n)
{
int m = 1;///m用来控制每一次填充表格时i和j的起始填充位置
for(int i = 1; i <= n; i++)///对日程表第一行进行初始化
maps[1][i] = i;
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++)
{
maps[i][j + (t - 1) * m * 2] = maps[i-m][j + (t - 1) * m * 2 - m];///左上角的值复制给右下角
maps[i][j + (t - 1) * m * 2 - m] = maps[i-m][j + (t - 1) * m * 2];///右上角的值复制给左下角
}
}
}
m *= 2;
}
}
int main()
{
int k;
while(~scanf("%d",&k))
{
int num, n = 1;
for(int i = 1; i <= k; i++)
n *= 2;
num = n;
solve(k, n);
for(int i = 1; i <= num; i++)
{
for(int j = 1; j <= num; j++)
printf("%2d ",maps[i][j]);
printf("\n");
}
}
return 0;
}