问题描写叙述:
设有n(n=2^k)支队伍參加循环赛,循环赛共进行n-1天,每支队伍要与其它n-1支队伍比赛一场,且每支队伍每天必须比赛一场,不能轮空。试按此要求为比赛安排日程。
算法思路:
我们先安排奇数下标位置与偶数下标位置之间的比赛,就有n/2场,方法非常easy,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,我们首先能够判定相应场次安排须要进行几次队伍分裂。方法非常easy,比如n=8,id=6,因为一次分烈得到n/2=4场,再次分裂可得n/4=2场,于是两次分裂就可以。同一时候id-4=2,也就是说两次分裂后的第2个赛场安排,这个2用于对子队伍移位计算使用。
參考代码:
- // team: 比赛安排结构,team[2k] vs team[2k+1]
- // len: team的总数
- // id: 第id轮的安排,id的范围[1, len-1]
- void game(int *team, int len, int id){
- int base = 2;
- while (id > len/base){
- id -= len/base;
- base <<= 1;
- }
- for (int i=0; i<base/2; ++i){
- int start = i+base/2+(id-1)*base;
- for (int j=0; j<len/base; ++j){
- team[i*2*len/base+2*j] = base*j+i;
- team[i*2*len/base+2*j+1] = (start+base*j)%len;
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////
- // 以下是測试部分
- void dump(int *arr, int len){
- for (int i=0; i<len; i+=2)
- printf("%02d-%02d ", arr[i], arr[i+1]);
- printf("/n");
- }
- int main(){
- const int len = 16;
- int team[len];
- for (int i=1; i<len; ++i){
- game(team, len, i);
- printf("[%02d] ", i);
- dump(team, len);
- }
- return 0;
- }
输出结果:
[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