问题描述
设有n=2k个选手进行循环赛,手工设计一个满足以下要求的比赛日程表:
(1) 每个选手必须与其他n-1个选手各赛一次;
(2) 每个选手一天只能赛一次;
(3) 循环赛一共进行n-1天。
算法思想
按分治策略,将所有的选手分为两半,n个选手的比赛日程表就可以通过为n/2个选手设计的比赛日程表来决定。递归地用对选手进行分割,直到只剩下2个选手时,比赛日程表的制定就变得很简单。这时只要让这2个选手进行比赛就可以了。
以下是思考的草稿图(有些乱但应该不影响看😋
算法设计
void GetTable(int k){
- 给出k=1时的二维数组,作为迭代的初始值;
- 根据二维数组右下角=左上角、右上角=左下角、左下角的元素值为左上角相应位置元素值加2^(k-1)的关系,自下而上地将k=n时的数组进行填充;
- 输出生成的二维数组即为所求比赛日程表。
}
算法分析
- 算法思想:按照分治策略,将所有参赛的选手分为两部分,n=2^k个选手的比赛日程表就可以通过为n/2=2^(k-1)个选手设计的比赛日程表来决定。递归地进行分割直到只剩下2个选手。
- 数组关系简述:求解的过程是自底向上的迭代过程,通过对k=1、2、3时二维数组的分析,可以得到二维数组右下角=左上角、右上角=左下角、左下角的元素值为左上角相应位置元素值加2^(k-1)的关系,进而通过循环实现题目需求。
- 时间复杂度:当n=1时,代码不需要进行循环,此时时间复杂度为O(1);当n>1时,外循环t从2到k,,内循环从m+1(相当于n+1)到n(相当于2n),执行次数为(n-1),其余三个并列的for循环均执行(n-1)次,可得时间复杂度为,即。综上,时间复杂度为:
完整代码
#include<iostream>
using namespace std;
void GetTable(int k){
int a[600][600]; //用二维数组表示比赛日程表
int i,j,n,t,m; //i:行数;j:列数;n:选手数目;t:循环量;m:已经安排的选手个数
n=2; //n从k=1开始
a[1][1]=1;
a[1][2]=2;
a[2][1]=2;
a[2][2]=1; //只有两位选手时的二维数组
for(t=2;t<=k;t++){ //对2^t位选手安排日程表
m=n; //将已经建立数组的m位选手存储
n=n*2; //当前所有选手数
for(i=m+1;i<=n;i++){ //对二维数组的左下角进行填写
for(j=1;j<=m;j++)
a[i][j]=a[i-m][j]+m; //左下角的元素值为左上角相应位置元素值加2^(k-1),即m
}
for(i=1;i<=m;i++){ //填写右上角,由算法设计可知右上角=左下角
for(j=m+1;j<=n;j++)
a[i][j]=a[j][i]; //与左下角同一位置元素对应
}
for(i=m+1;i<=n;i++){ //填写右下角,由算法设计可知右下角=左上角
for(j=m+1;j<=n;j++)
a[i][j]=a[i-m][j-m]; //与左上角同一位置元素对应
}
}
cout<<"参加比赛的选手共有 "<<n<<" 位"<<endl;
for(i=1;i<=n;i++){ //输出所得的二维数组
for(j=1;j<=n;j++){
cout<<a[i][j]<<" ";
}
cout<<endl;
}
}
int main(){
int k;
cout<<"请输入k的值(n=2^k):";
cin>>k;
cout<<"比赛日程表如该二维数组所示:" <<endl;
if(k!=0){
GetTable(k);
}
cout<<"其中,第i行第j列表示第i位选手在第j-1天比赛的选手编号(i,j均由下标1算起)。";
return 0;
}
结果演示