循環賽日程表


一、問題描述

設有 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

四、內容來源

算法之循環賽日程表


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM