循环赛日程表

一、问题描述

设有 n = 2k 个运动员要进行网球循环赛。现在要设计一个满足以下要求的比赛日程表

  1. 每个选手必须与其他 n-1 个选手各赛一场
  2. 每个选手一天只能比赛一场
  3. 循环赛一共进行 n-1 天

二、算法分析

按此要求可将比赛日程表设计成 n 行 n-1 列的表,在表中第 i 行和第 j 列处填入第 i 个选手在第 j 天所遇到的对手。


按分治策略,可以将所有的选手分为两半,则 n 个选手的比赛日程表可以通过 n/2 个选手的比赛日程表来决定。递归地用一分为二的策略对选手进行划分,直到只剩下两个选手时,比赛日程表的制定就变得很简单,这时只要让这两个选手进行比赛就可以了。

通过 k 增长来看算法实现步骤:

  1. 当 k = 1 时,n = 21 = 2 人,循环表为

    1  2

    2  1

  2. 当 k = 2 时,n = 22 = 4 人,循环表为

    1  2  3  4

    2  1  4  3

    3  4  1  2

    4  3  2  1

  3. 当 k = 3 时,n = 23 = 8 人,循环表为

    1  2  3  4  5  6  7  8

    2  1  4  3  6  5  8  7

    3  4  1  2  7  8  5  6

    4  3  2  1  8  7  6  5

    5  6  7  8  1  2  3  4

    6  5  8  7  2  1  4  3

    7  8  5  6  3  4  1  2

    8  7  6  5  4  3  2  1

以此类推,可以用分治的方法实现,先自顶向下分解,直到分解到最简单的情况,即人数为 2 人,这时就可以两两比赛,表的填充为对角填充的方式,然后再自底向上填充表格,具体的看上面的 k = 1、k = 2、k = 3 时形成的循环表就很好理解了。

三、代码实现

#include <stdio.h>

#define N 64

void GameTable(int k, int a[][N])
{
    int n = 2; // 选手数
    
    // 求解两个选手比赛日,得到左上角元素
    a[0][0] = 1;  a[0][1] = 2;
    a[1][0] = 2;  a[1][1] = 1;
    
    int i, j, half;
    
    // 循环处理,依次处理 2^2 ... 2^k 个选手比赛日程
    for (int t = 1; t < k; t++) {
        
        half = n; // 当前选手数的 1 / 2
        n *= 2;   // 当前选手数
        
        // 左下角
        for (i = half; i < n; i++) // 行
            for (j = 0; j < half; j++) // 列
                a[i][j] = a[i - half][j] + half;
        
        // 右上角
        for (i = 0; i < half; i++)
            for (j = half; j < n; j++)
                a[i][j] = a[i + half][j - half];
        
        // 右下角
        for (i = half; i < n; i++)
            for (j = half; j < n; j++)
                a[i][j] = a[i - half][j - half];
    }
    
    printf("运动员编号\t");
    for (i = 1; i < n; i++) {
        printf("第 %d 天\t", i);
    }
    printf("\n\n");
    
    for (i = 0; i < n; i++) {
        printf("  %d 号  \t", i + 1);
       
        for (j = 1; j < n; j++)
            printf("       %d", a[i][j]);
    
        printf("\n");
    }
}

int main()
{
    int a[N][N] = { 0 };
    int k = 3;
    
    printf("******************************************\n");
    printf("\t\t**\t\t循环赛日程表\t\t**\n");
    printf("******************************************\n\n");
    
    GameTable(k, a);
    
    return 0;
}

******************************************
        **      循环赛日程表      **
******************************************

运动员编号   第 1 天 第 2 天  第 3 天  第 4 天   第 5 天   第 6 天   第 7 天   

  1 号     2       3       4       5       6       7       8
  2 号     1       4       3       6       5       8       7
  3 号     4       1       2       7       8       5       6
  4 号     3       2       1       8       7       6       5
  5 号     6       7       8       1       2       3       4
  6 号     5       8       7       2       1       4       3
  7 号     8       5       6       3       4       1       2
  8 号     7       6       5       4       3       2       1

四、内容来源

算法之循环赛日程表

posted @ 2020-03-15 13:40  和风细羽  阅读(3844)  评论(0编辑  收藏  举报