算法:循环赛日程表_一般化(n可以为奇数,也可以为偶数)

算法思路:

  算法思路(N可能为奇数,也可能是偶数)

总体思路:按分治策略,将所有分为两半,n个选手可以通过n/2个选手设计的比赛日程表来决定。递归地用一分为二的略对选手进行分割,直到只剩下两个选手。

对于N为奇数的情况可以虚拟多一个选手,使其编程N+1个选手的日程表,最然后忽略虚拟运动员参与的比赛。对于分割时候N/2的情况也做特殊处理, 前n/2轮比赛空选手与下一个未参赛的选手进行比赛
存储结构:

数组 a[i][j], a[i][j]表示运动员i在第j天所遇到的选手

详细思路:

详细思路:

(1)分治法,

    Tourna(n):

If n==1  

       a[1][1]=1;return;

 

tourna(n/2);//递归分割

copy(n); //填表

 

(2)n为偶数的情况Copy()函数:A.将左上角递归计算出的小块的所有数字按其相对位置抄到右下角,B.将右上角的小块的所有数字加n/2后按其相对位置抄到左下角,将左下角的小块中的所有数字按其相对位置抄到右上角

Viod copy(int n){

   Int m=n/2;

   For i=1àm

      For j=1--->m{

   a[i][j+m]=a[i][j]+m;// 小块的数值抄到右下角

   a[i+m][j]=a[i][j+m];// 右上抄到左下

   a[i+m][j+m]=a[i][j];//   左下抄到右上

}

        

}

----------------------------------------------------------------------------------------------------------------------

(3)一般性描述:n为偶数; n是奇数时增加一个虚拟选手n+1,将问题转换为n是偶数的情形。

   tournament(n):

       if n==1 : a[1][1]=1;return;//分割到最后

       if n为奇数 :tournament(n+1);return;//奇数的情况加上虚拟选手

       tournament(n/2);//分割

       makecopy(n);//这个函数copy分n为偶数很n为奇数的情况

(4) 判断奇偶

odd(n):

      Return n&1;

(5)makecopy()与copy相似,并区分奇偶情况

     makecopy(n):

        if n/2>1 &&add(n/2) copyodd(n);//对n/2为奇数的情况的处理

        else copy(n);//偶数的情况

 (6)copyodd(n)实现n/2为奇数的时候的复制

    n/2奇数的一种处理方法:前n/2轮比赛空选手与下一个未参赛的选手进行比赛


 

 

    Copyodd(n):

       Int m=n/2

       For i=1→m

         b[i]=m+i;b[m+i]=b[i];

      

       for i=1→m

         for j=1→m+1{

            if a[i][j]>m:

                 a[i][j]=b[i];a[m+i][j]=(b[i]+m)%n;

            else

               a[m+i][j]=a[i][j]+m;

            for j=2→m

                a[i][m+j]=b[i+j-1];

                a[b[i+j-1]][m+j]=i

 

          }

三、时间效率:

 T(n)=T(n/2)+f(n)

 f(n)为copy的时间

f(n)=(n/2)^2

推出:T(n)=T(n/2)+(n/2)^2

N规模的问题做logN次f(n)

T(n)属于O(∑O((n/(2^k))^2)) 1<=k<log(n) 也就是T(n)∈O(n^2)

如有不懂参考这位博主的详解:
点击进入
C++代码实现如下:

#include<bits/stdc++.h>
using namespace std;
const int size = 50;
//数组 a[i][j], a[i][j]表示运动员i在第j天所遇到的选手 
int a[size][size];
//运动员个数 
int n;
//判断奇偶 
int odd(int n)//奇数时返回1 
{
	if(n%2==1)
		return 1;
	else 
		return 0;
}
//打印结果
void print() 
{
	if(odd(n))   // n为奇数和偶数输出情况不同,要分别考虑
	{
		for(int i = 1; i<=n; i++)
		{
			for(int j = 1; j<=n+1; j++)
				if(a[i][j] == n+1)
					cout << "0  ";
				else
					cout << a[i][j] << "  " ;
			cout << endl;
		}
	}
	else
	{
		for(int i = 1; i<=n; i++)
		{
			for(int j = 1; j<=n; j++)
				cout << a[i][j] << "  " ;
			cout << endl;
		}
	}
}
// 实现n/2为奇数时的复制
void copyodd(int n)        
{
	int b[size];
	int m = n/2;
	/*
	因为2m+1已经超出n的范围,所以我们将值为m+1和2m+1的队组成一组,即1—m+1,2—m+2,3—m+3,……,m—n. 
因为它们之间都相差m,所以可以用一个数组b的索引和值的对应关系来表示: 
	*/
	for(int i = 1; i<=m; i++)
	{
		b[i] = m+i;
		b[m+i] = b[i];
	}
	/*1--m行+m得到m+1--n行的值*/
	for(int i = 1; i<=m; i++)
	{
		for(int j=1; j<=m+1; j++)
		{
			if(a[i][j] > m)
			{
				a[i][j] = b[i];
				a[m+i][j] = (b[i] + m)%n;
			}
			else
				a[m+i][j] = a[i][j] + m;
		}
		for(int j = 2; j<=m; j++)
		{//在m+1到n之间循环取值,且每次取m-1个 
			a[i][m+j] = b[i+j-1];
			a[b[i+j-1]][m+j] = i;//剩下未分配的随之解决
		}
	}
}
//n为偶数的情况copy()函数
void copy(int n)
{
	int m = n/2;
	for(int i = 1; i<=m; i++)
		for(int j = 1; j<=m; j++)
		{
			a[i][j+m] = a[i][j] + m;//相应位置加2放到右上角 
			a[i+m][j] = a[i][j+m];//右上抄到左下 
			a[i+m][j+m] = a[i][j];//左上复制到右下 
		}
}
//makecopy 与copy算法类似,但是要区分n/2为奇数或偶数的情形
void makecopy(int n)  
{
	if(n/2 > 1 && odd(n/2))
		copyodd(n);//对n/2为奇数时处理 
	else
		copy(n);//偶数时处理 
}
//分治法
void tournament(int n)
{
	if(n == 1)
	{
		a[1][1] = 1;
		return;
	}
	if(odd(n))
	{
		tournament(n+1);
		return;
	}
	tournament(n/2);
	makecopy(n);
} 
int main()
{
	int i,j;
	cin >> n;
	tournament(n);
	print(); 
}

结果:
在这里插入图片描述
在这里插入图片描述