初衷
在教材上看到这个问题的时候,对于奇数的处理百思不得其解,然而网上的答案要么就是n=2k的情况,要么就是自己根本都没有理解,给你讲了一大堆,各种情况,很麻烦,甚至有些是错的误人子弟。所以写下这篇思路,分享给各位。其实这个问题的核心就是分治的治该怎么去构造的问题。
问题
设有N个运动员要进行网球循环赛,设计一个满足以下要求的比赛日程表
(1)每个选手必须与其他n-1个选手各赛一次
(2)每个选手一天只能赛一次
(3)当n 是偶数,循环赛进行n-1天,当n是奇数,循环赛进行n天。
算法分析
- 我们采用分治法,先算出n/2的情况,然后进行合并,构造出n的情况。难点就在于构造过程,设第i个选手第j天比赛的队员为A[i][j]。
- 若m = n/2为偶数,这时候我们知道偶数人数已经算出了前m个队员的前passed_days天(对于偶数而言是m-1,对于奇数是m)的比赛情况,我们怎么构造呢?
- 先横向构造,也就是构造出后m个队员在前m-1天的比赛情况,那么为了保证不重复我们采用递增的构造方式,让i+m号选手与比A[i][j]大m的选手比赛,也是是说
- A[i + m][j] = A[i][j] + m; (1≤i≤m,1≤j≤passed_days,i代表队员,j代表当前比赛的天数)
- 可以看出来一定不会重复,因为前m个队员之前从未与后m个队员比赛过,然后A[i][j]彼此又是互不相同的,所以A[i+m]彼此也一定不相同
- 再纵向构造,设n个队员比赛所需总天数为days(对于偶数而言是n-1,对于奇数是n),也就是说构造n个队员在后(days - passed_days)天的比赛情况,同样为了保证不重复我们也采用增量构造,
- passed_days +1≤j≤days,
- rvalue = (count + i-1)%m + m+1;//保证i队员与后面的队员(rvalue必然大于m)比赛,这样就与前面passed_days天的比赛不重复
- count为增量初始值为0,j每加1,count++
- A[i][j] = rvalue;
- A[rvalue][j] = i; //因为是两两比赛,后面m对用中的与之对应个队员直接构造出来
- 需要注意的是纵向构造的时候我们先构造A[1][j]也就是说先保证第一个队员在(days - passed_days)比赛的队员肯定与之前(passed_days)是不同的,那么由于i也是递增的所以,A[i][j]彼此之间必定也是互不相同的!
- 先横向构造,也就是构造出后m个队员在前m-1天的比赛情况,那么为了保证不重复我们采用递增的构造方式,让i+m号选手与比A[i][j]大m的选手比赛,也是是说
- 举个栗子:假设n=4.
先计算n/2 = 2,我们知道A[1][1] = 2,A[2][1] = 1(偶数比赛只有一天)
1 2(队员编号)
2 1(第一天)
复制代码