(Day 5)循环赛日程表(分治策略)

循环赛日程表

一.问题描叙
设有n=2^k个运动员,要进行网球循环赛。现在要设计一个满足以下要求的比赛日程表
(1).每个选手必须与其他n-1个选手各赛一场
(2).每个选手一天只能赛一次
(3).循环赛一共进行n-1天
分析:
下表中,行表示选手编号,列表示比赛的天数,以第一行为例,表示1号选手分别与2、3、…、n号选手比赛
当k=1时,n=2
在这里插入图片描述
当k=2时,n=4
在这里插入图片描述
研究发现,上面的表有一定的规律性,每一个n*n(n=2、3、…、n)的方阵中,其对角线的板块数字相同。
思路: 先建立一个二维数组,并对数组第0列进行赋值,依次为选手的编号1、2、…、n,这些值是为了后面对角线复制时使用的。请继续往下看。
(1) 根据分治的思想,将问题不断地一分为二,得到规模更小的子问题,直到n=1,比如n=4时,将其分成问题1(1,2)和问题2(3,4),两个子问题继续一分为二,最终得到4个子问题问题1(1)、问题2(2)、问题3(3)、问题4(4),此时各个子问题的n=1,这些问题不用解决可以直接返回。返回上一层后,用一个合并函数将相应的子问题的解合并起来。(n=1时,看似没有对问题进行解决,实质上我们是在返回到上一层,合并解的时候解决的问题,请继续往下面看)
(2) 编写一个可以合并子问题解的函数,并且这个函数还可以起到复制方阵对角线的值的功能,即将左上角的值复制给右下角,将左下角的值复制到右上角。因为合并的过程实质上就是复制对角线的过程。(对角线赋值时的数组下标关系,大家可以草稿纸上画格子稍微分析一下即可明白)
(3)先对每个问题的左上角和左下角进行解决,因为当左上角的值确定之后,就可以通过对角线复制得到右下角的值,同理,当左下角的值确定之后,就可以通过对角线复制得到右上角的值。如何解决左上角和左下角的值呢?其实很简单,因为二维数组的最左边一列已经在最开始的时候赋过值了,因此当n=2时,调用合并复制函数时,左上角和左下角的值其实就是最左边即第0列的值,很显然,这是已经通过前期赋值所确定的值,那接下来就可以直接通过对角线赋值得到右上角和右下角的值。
在这里插入图片描述
上述过程跟归并排序的流程有些相似之处(先分成到最小子问题,再合并求解)。大家可以研究对比一下
下面的代码建议大家先从主函数开始阅读,按照函数调用的顺序阅读更容易分析思路。

#include <iostream>
#include <cmath> 
#define MAX 100 //假设最多100位选手 
using namespace std;
int a[MAX][MAX];//二维数组作为全局变量,可以免去数组作为参数的麻烦
void mergeCopy(int tr,int tc,int size){  //tr,tc为左上角顶点坐标, size为人数 
	int s=size/2;
	//对右上角赋值 
	for(int i=tr;i<tr+s;i++)       //  i的范围:[tr,tr+s) 
		for(int j=tc+s;j<size;j++)//  j的范围:[tc+s,size)
			a[i][j]=a[i+s][j-s];
	//对右下角赋值 
	for(int i=tr+s;i<tr+size;i++)  // i的范围:[tr+s,tr+size)
		for(int j=tc+s;j<tc+size;j++)// j的范围:[tc+s,tc+size)
			a[i][j]=a[i-s][j-s];
}
void gameTable(int tr,int tc,int size){
	if(size==1){   //若只有一个选手,直接返回 
		return;
	}
	int s=size/2;
	gameTable(tr,tc,s);     //解决左上角的值:编号为1~s的选手 
	gameTable(tr+s,tc,s);  //解决左下角的值:编号为s+1~size的选手 
	//左上角和左下角安排好后,即可按照对角线相等的规律来复制 
	//即合并结果 
	mergeCopy(tr,tc,size); 
}
int main(){
	int k;
	cin>>k;
	int n=pow(2,k);  //比赛人数 
	for(int i=0;i<=n;i++){ //对各个选手编号,即数组第0列为1,2,3,....,n 
		a[i][0]=i+1;
	}
	gameTable(0,0,n);
	
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
} 
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值