循环赛日程表
问题:
(先声明一点,下面我写了两种代码,但是都只能满足 队伍数 = 2的幂次方 的情况)
一年一度的欧洲冠军杯马上就要打响,在初赛阶段采用循环制,设共有n队参加,初赛共进行(n-1)天,每队要和其他各队进行一场比赛。要求每队每天只能进行一场比赛,并且不能轮空。请按照上述需求安排比赛日程,决定每天各队的对手。
对问题的理解
- 根据排列组合,n 个队伍总共要比赛 n(n-1)/2 场
- 初赛共进行(n-1)天,那么显而易见每天要比赛 n/2 场
- 显而易见,会看出一个问题来:这样的话,n 必须为偶数(因为比赛的场数得为整数)
扩充:
- 若n为奇数会怎么样?
- 首先,n队参加,由于每队每天只能进行一场比赛,则至少要比赛(n-1)天,这个很容易理解
- 已知n为奇数,比赛天数不能是(n-1)天
- 那么,若比赛天数为 n 天,则每天要比赛 (n-1)/2 场,这样就没问题了
循环赛日程有这么一个规律:
- 若比赛队数:n恰好为 2的k次方
- 则可以直接使用分治算法解决这个问题
若 n = 2
- 第一列的属性是队伍名
- 第二列属性是 第一天
- 第一行第二个,表示 1 打 2
- 第二行第二个,表示 2 打 1
- 这两个表示的是一个意思,在循环表中
若 n = 4
- 第一列是属性
- 第二列到第四列分别代表:第一天到第三天
- 比如第一行第4个,表示在第三天,1打4
以此类推…
可以看出,在表格中
- 左上角 = 右下角
- 左下角 = 右上角
- 规律从 n = 2 开始就成立
- 所以可以用分治算法解决这问题
此代码只适用于 队伍数 = 2的幂次方的
#include<iOStream>
#include<stdio.h>
using namespace std;
#define MAXSIZE 64 //暂时允许的最多的参赛队伍数量
int a[MAXSIZE][MAXSIZE]; //将比赛情况放在这个二维表中
int GameTable(int n,int k){
if(n == 2){ //子问题足够小时
a[k][0] = k+1;
a[k][1] = k+2;
a[k+1][0] = k+2;
a[k+1][1] = k+1;
}
else{
GameTable(n/2,k); //划分子问题
GameTable(n/2,k+n/2);
for(int i = k; i < k+n/2; i++){ //填充二维表的右上角
for(int j = n/2; j < n; j++){
a[i][j] = a[i+n/2][j-n/2];
}
}
for(int i = k+n/2; i < k+n; i++){ //填充二维表的右下角
for(int j = n/2; j < n; j++){
a[i][j] = a[i-n/2][j-n/2];
}
}
}
}
int main(){
int n; //队伍数
cout<<"please input the number of participating teams(2的幂次方,2到64):";
cin>>n;
GameTable(n,0);
cout<<"编号 ";
for(int i = 1; i <= n; i++)
cout<<"第"<<i<<"天 ";
cout<<endl;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
printf("%4d",a[i][j]);
}
cout<<endl;
}
}
若一时间不太了解安排循环赛日程中的规律:
管他呀的,直接暴力破解,三重for循环搞起
- 下面的代码我想了很久,还是只能做到对 n = 2的幂次方 的情况
- 当n为其他情况时,按下面代码分配一定会造成遗漏,循环表分配是不正确的
#include<iostream>
#include<string.h>
using namespace std;
#define MAXSIZE 64 //暂时限定队伍最大的数量
int main(){
while(1){
int team[MAXSIZE+1][MAXSIZE+1], //team不使用下标为零的空间
//team存放已比赛过的队伍
op[MAXSIZE/2][2],//用来输出当天的比赛情况
s[MAXSIZE]; //用来检验当日已比赛的队伍
int num; //参赛队伍的数量
int t;
do{
cout<<"请输入参赛队伍的数量:";
cin>>num;
}while(num < 2);
memset(team,0,sizeof(team));
for(int i = 1; i < num; i++){
memset(s,0,sizeof(s));
memset(op,0,sizeof(op));
t = 0;
for(int j = 1; j <= num; j++){ //表示第i天的第j支队伍的比赛情况
if(s[j] == 0){
for(int k = j+1; k <= num; k++){ //k表示和j配对的对手
if(team[j][k] == 1 || s[k] == 1)continue;
op[t][0] = j;
op[t++][1] = k;
s[k] = 1;
team[j][k] = 1;
break;
}
}
if(t == num/2)break; //每天比赛场数满了,进入下一天
}
cout<<"第"<<i<<"天:";
for(int p = 0; p < t; p++)cout<<"("<<op[p][0]<<","<<op[p][1]<<") ";
cout<<endl;
}
}
}
参考资料:
《算法学习与应用 从入门到精通》张玲玲
分治法——循环赛日程安排问题
算法训练 比赛安排
分治法:循环赛日程安排问题
相关阅读
#!/usr/bin/python3# 文件名: 单循环赛制# 作者:巧若拙# 时间:2019-01-23'''单循环赛制是一种较为公平合理的比赛制度,比赛过程中所
一、问题:设有n=2^k个运动员,要进行网球循环赛。现在要设计一个满足以下要求的比赛日程表(1)每个选手必须与其他n-1个选手各赛一场(2)每
单循环赛,是指所有参赛队伍都需跟其他队伍比赛一次,根据比赛得分,胜负场次来排列名次。比赛队伍为单数时,轮数等于队伍数,为双数时,轮数