算法之循環賽日程表


循環賽日程表

一.問題描敘

    設有n=2^k個運動員,要進行網球循環賽。現在要設計一個滿足以下要求的比賽日程表

    (1).每個選手必須與其他n-1個選手各賽一場

    (2).每個選手一天只能賽一次

    (3).循環賽一共進行n-1天

二.問題分析

    按此要求可將比賽日程表設計成n行n-1列的表,在表中第 i 行和第j 列處填入第 i 個選手在第 j 天所遇到的對手。

    例如,當選手的人數為8人時,其比賽日程表如下圖

    

    算法分析:按分治策略,我們可以將所有的選手分為兩半,則n個選手的比賽日程表可以通過n/2個選手的比賽日程表來決定。遞歸地用這種一分為二的策略對選手進行划分,直到只剩下兩個選手時,比賽日程表的制定就變得很簡單。這時只要讓這兩個選手進行比賽就可以了。如上圖,所列出的正方形表是8個選手的比賽日程表。其中左上角與左下角的兩小塊分別為選手1至選手4和選手5至選手8前3天的比賽日程。據此,將左上角小塊中的所有數字按其相對位置抄到右下角,又將左下角小塊中的所有數字按其相對位置抄到右上角,這樣我們就分別安排好了選手1至選手4和選手5至選手8在后4天的比賽日程。依此思想容易將這個比賽日程表推廣到具有任意多個選手的情形。

   算法實現步驟:

 (1)當k=1時,即人數為2人,此情況為最簡單的情況

         此時表為

            1  2

            2  1

   (2)當k=2時,人數為4人,循環表為

            1  2  3  4

            2  1  4  3

            3  4  1  2  

            4  3  2  1

    (3)當k=3時,人數為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時形成的循環表就很好理解了。

三.源代碼展示

 1 #include<stdio.h>
 2 #include<math.h>
 3 #define N 50
 4 void GameTable(int k,int array[][N]);
 5 void print(int k,int array[][N]);         //輸出二維數組 
 6 main()
 7 {
 8     int k;
 9     int array[N][N];
10     printf("\t\t****************************************\n");
11     printf("\t\t**\t\t循環賽日程表          **\n");
12     printf("\t\t****************************************\n\n");
13     printf("設參賽選手的人數為n(n=2^k),請輸入k 的值:");
14     do
15     {
16          scanf("%d",&k);
17         if(k!=0)
18         {
19             GameTable(k,array);
20             print(k,array);
21         }
22         else
23           printf("您輸入的數據有誤,請重新輸入"); 
24     }while(k!=0);
25 
26 }
27 void GameTable(int k,int array[][N])//數組下標從1開始
28 {
29     int i,j,s,t;
30     int n=1;
31     for(i=1;i<=k;i++)
32         n*=2;                       //求總人數
33     for(i=1;i<=n;i++)
34         array[1][i]=i;                  //第一行排1-8
35     int m=1;                          //用來控制每一次填表時i行j列的起始填充位置
36     for(s=1;s<=k;s++)                 //s指對稱賦值的總循環次數,即分成幾大步進行制作日程表
37     {
38         n=n/2;
39         for(t=1;t<=n;t++)              //t指明內部對稱賦值的循環次數
40             for(i=m+1;i<=2*m;i++)
41                 for(j=m+1;j<=2*m;j++)
42                 {
43                     array[i][j+(t-1)*m*2]=array[i-m][j+(t-1)*m*2-m];       //右上角等於左上角的值
44                     array[i][j+(t-1)*m*2-m]=array[i-m][j+(t-1)*m*2];       //左下角等於右上角的值
45                 }
46         m*=2;
47     }
48     
49 }
50 void print(int k,int array[][N])
51 {
52     int i,j;
53     int num=pow(2,k);
54     printf("%d人的循環賽日程表如下\n",num);
55     for(i=1;i<=num;i++)                           //輸出二維數組 
56     {
57         for(j=1;j<=num;j++)
58         {
59             printf("%d\t",array[i][j]);
60         }
61          printf("\n");
62     }
63 } 

四.程序結果展示

                     

 


免責聲明!

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



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