循环赛日程安排问题

问题描述:
    设有n(n=2^k)支队伍参加循环赛,循环赛共进行n-1天,每支队伍要与其他n-1支队伍比赛一场,且每支队伍每天必须比赛一场,不能轮空。试按此要求为比赛安排日程。

 

算法思路:
   我们先安排奇数下标位置与偶数下标位置之间的比赛,就有n/2场,方法很简单,team[2k]=2k,所有奇数号组成一个序列[1,3...n-1],然后循环移动n/2-1次(比如第2个序列就是[3,5...n-1,1]),然后将该序列填充在team的奇数位置上。

   接下来将队伍一分为二,奇数为一组,偶数为一组,分配安排其内部比赛(因为奇偶数之间前面已经安排过了啦)。以奇数组[1,3,5,7]为例(以n=8为例说明),我们仍然先安排奇数下标位置与偶数下标位置之间的比赛,也就是[15]与[37]之间的比赛,共有2场(n/4)。

   接下来,再将队伍一分为二,得到[15],[37],[04],[26],对每一部分,仍然是先安排奇数下标位置与偶数下标位置之间的比赛,共1场(n/8)。此时已不可再分出子队伍,计算结束。

    

对比赛安排编号:

    从前文的分析可以看出,我们产生比赛日程安排是有规律可循,先产生n/2,然后是n/4,...直到最后1场。由于n=2^k,那么这些安排场次总数为2^(k-1)+2^(k-2)...+1=2^k-1=n-1,恰好相符(事实上是必然的)。

    这样,对于给定一个编号id,我们首先可以判定对应场次安排需要进行几次队伍分裂。方法很简单,例如n=8,id=6,由于一次分烈得到n/2=4场,再次分裂可得n/4=2场,于是两次分裂即可。同时id-4=2,也就是说两次分裂后的第2个赛场安排,这个2用于对子队伍移位计算使用。

 

参考代码:


 

 

  1. // team: 比赛安排结构,team[2k] vs team[2k+1] 
  2. // len:  team的总数 
  3. // id:   第id轮的安排,id的范围[1, len-1] 
  4. void game(int *team, int len, int id){
  5.     int base = 2;
  6.     while (id > len/base){
  7.         id -= len/base;
  8.         base <<= 1;
  9.     }
  10.     for (int i=0; i<base/2; ++i){
  11.         int start = i+base/2+(id-1)*base;
  12.         for (int j=0; j<len/base; ++j){
  13.             team[i*2*len/base+2*j] = base*j+i;
  14.             team[i*2*len/base+2*j+1] = (start+base*j)%len;
  15.         }
  16.     }
  17. }
  18. // 下面是测试部分
  19. void dump(int *arr, int len){
  20.     for (int i=0; i<len; i+=2)
  21.         printf("%02d-%02d ", arr[i], arr[i+1]);
  22.     printf("/n");
  23. }
  24. int main(){
  25.     const int len = 16;
  26.     int team[len];
  27.     for (int i=1; i<len; ++i){
  28.         game(team, len, i);
  29.         printf("[%02d] ", i);
  30.         dump(team, len);
  31.     }
  32.     return 0;
  33. }

输出结果:

[01] 00-01 02-03 04-05 06-07 08-09 10-11 12-13 14-15
[02] 00-03 02-05 04-07 06-09 08-11 10-13 12-15 14-01
[03] 00-05 02-07 04-09 06-11 08-13 10-15 12-01 14-03
[04] 00-07 02-09 04-11 06-13 08-15 10-01 12-03 14-05
[05] 00-09 02-11 04-13 06-15 08-01 10-03 12-05 14-07
[06] 00-11 02-13 04-15 06-01 08-03 10-05 12-07 14-09
[07] 00-13 02-15 04-01 06-03 08-05 10-07 12-09 14-11
[08] 00-15 02-01 04-03 06-05 08-07 10-09 12-11 14-13
[09] 00-02 04-06 08-10 12-14 01-03 05-07 09-11 13-15
[10] 00-06 04-10 08-14 12-02 01-07 05-11 09-15 13-03
[11] 00-10 04-14 08-02 12-06 01-11 05-15 09-03 13-07
[12] 00-14 04-02 08-06 12-10 01-15 05-03 09-07 13-11
[13] 00-04 08-12 01-05 09-13 02-06 10-14 03-07 11-15
[14] 00-12 08-04 01-13 09-05 02-14 10-06 03-15 11-07
[15] 00-08 01-09 02-10 03-11 04-12 05-13 06-14 07-15

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值