问题:
有N个运动员进行单循环赛,即每个运动员要和所有其他运动员进行一次比赛。
1.试用分治法为N个运动员安排比赛日程。
2.要求每个(或队)运动员每天只能进行一场比赛,且当运动员人数(队数)为偶数时,整个比赛在N-1天内结束,为奇数时,在N天内结束;
3.将运动员从1到N编号。
————————————————
思路:
我们用表格的方式来表示循环赛的日程安排,最左边的一列表示队号,每一行表示相应队比赛每天的对手,即a[i][j]表示第i队第j天的对手。
我们从两个队的比赛开始,并发现其中规律
while i != 1 do
if i % 2 == 0
i i/2
else i i+1
对于任何大于2的数除以2再加1或者加1再除以2,它的规模都会缩小,所以这最终是可以终止的。接着我们看是否能分解为上面两种情况:
n为奇数时,用n+1代替计算,即转化为偶数的队数。
n为偶数时,分两种情况,(1)n/2为偶数,(2)n/2为奇数。(1)继续被2除,直到商为1,即转化为第一种情况,n是2的幂,或者商为大于1的奇数(2),转化为(2)。(2)就是第二种情况。所以,任意n是可以分解为上面两种情况的,而且是可以终止的。
把上述的内容一般化,即为我们解决该问题的解:
1.tournament(a[][],n)用递归把问题分解为多个子问题,其中odd(n)判断n是否为奇数,makecopy(a[][], n)用上面的两种情况中的一种产生日程:
tournament(a[][], n)
if n == 1
a[0][0] = 1;
return;
if odd(n)
tournament(a[][], n+1);
else
tournament(a[][], n/2);
makecopy(a[][], n);
return;
2.odd(n)判断n是否为奇数:
odd(n)
return n&1;
3.makecopy(a[][], n)用上面的两种情况中的一种产生日程,如果n/2为偶数用第一种,即copy(a[][], n),n/2为奇数用第二种,即copyodd(a[][], n):
makecopy(a[][], n)
m = n/2;
if odd(m)
copyodd(a[][], n);
else
copy(a[][], n);
至此,我们完成了部分对应关系,接着要解决m+2到n列的对应关系:
第1行还缺m+2……n;
第2行缺 m+3……n, m+1;
第3行缺 m+4……n, m+1,m+2;
……
第m行缺 m+1……n-1.
可以看出它们是在m+1到n之间循环取值,且每次取m-1个。所以,可以用一个包括2个m+1到n连续排列的数组表示:
0 1 …… m-1 m m+1 …… n-1
m+1 m+2 …… n m+1 m+2 …… n
剩下未分配的随之解决。
copyodd(a[][], n)
m = n/2;
for i0 to n-1 do
b[i] = m+i+1;
for i0 to m-1 do
for j 0 to m-1 do
if a[i][j]> m
a[i][j] = b[i];
a[i+m][j] = i+1;
else
a[i+m][j] = a[i][j] + m;
for j 1 to m-1
a[i][m+j] = b[i+j];
a[b[i+j]][m+j] = i+1;
循环赛算法到此完成。
数据结构:
一个二维数组a[n][n]存储日程表,一个一维数组b[n]存储映射关系。
效率分析:
T(n) = T(n/2)+f(n)
其中,f(n)为copy()的时间
f(n) = (n/2)^2
推出:T(n) = T(n/2)+(n/2)^2
因为二分可以分log n次,所以T(n) = (n/2)^2+(n/4)^2+…+1 ;
所以,T(n)∈O(n^2)。