算法-日程表

博客详细介绍了如何使用分治策略设计一个网球循环赛的比赛日程表,该表满足每个选手与其他所有选手各赛一次且每天只赛一次的要求。通过递归将选手对半分组,逐步构建日程表,并提供了具体的代码实现。文章指出,理解代码的关键在于掌握交叉填表的逻辑,建议读者运行代码以加深理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 题目

设有n-2个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:
①每个选手必须与其他n-1个选手各赛一次;
②每个选手一天只能赛一次;
循环赛一共进行n-1天。
按此要求可将比赛日程表设计成有n行和n-1列的表。在表中第i行和第j列处填入第i个选手在第j天所遇到的选手。

2 思路

按分治策略,可以将所有选手对分为两半,n个选手的比赛日程表就可以通过为n/2个选手设计的比赛日程表来决定。递归地用这种一分为二的策略对选手进行分割,直到只剩下两个选手时,比赛日程表的制定就变得简单了。这时只要让这两个选手进行比赛就可以了。
如果是n=2*可以对称分割,则可以这样排:
在这里插入图片描述

3 分析

主要是代码太多循环了,有点难分析。

3.1 各个参数的含义:

  • n:n=2^k,有n个选手(保证了n为偶数)
  • k:划分表,一共会划分k次(每次划分都是把n*n的表化成四个(n/2)*(n/2)嘛,很好理解)

3.2 四个循环:

  1. 里面的两个for循环:交叉填m*m大小的日程表(运行一遍我的代码就知道了,每次交叉填完就输出日程表)
  2. 第二层循环:因为最开始肯定m=1,把第一行的交叉填到第二行,一共会有n/2次这样的交叉填,第二层循环参数 t 就是用来控制这个交叉填次数的。
  3. 最外层循环:第二行填完了,就是第一次递归完成了;接下来将第一二行填到第三四行,其实这就是递归的第二层了,递归一共会有 k 层,所以原代码是外层是 for 循环 k 控制,我习惯写成 while,感觉更好理解;

3.3 交叉填为啥是那样写的:

我们每次填的时候会发现,他的行其实就是i=m+1 ~ i<=2m(因为每次填mm大小,所以最下行=最上行+m-1)。
但是清注意列虽然也是m宽,这个代码 j 的初始大小永远都是m+1,而列实际上是随着前面的交叉填填完后要后移2*m的,因此代码就是如下所写了。

3.4 建议

看文字确实很难理解,运行一遍我的代码就会清楚很多。
希望能帮到大家。(外加,算法好难,哭)

4 代码

#include<iostream>
using namespace std;
int a[10][10];//日程表 

void Table(int k) { //有2^k个选手 a为比赛日程表
	int n=1;
	for(int i=1; i<=k; i++) //计算选手人数(n=2^k)
		n=n*2;
	for(int i=1; i<=n; i++) //初始化比赛日程表:设置日程表第一行
		a[1][i]=i;
	int m=1;//每次填充时,起始填充位置
	while(k--) { //问题被划分k次
		n/=2; 		//n=交叉填的次数
		//k=3 填第二行:     需要填 原n/2次  交叉填 因此for循环是t<=n 
		//k=2 填第三、四行: 只需要 原n/4次  交叉填
		//k=1 填五六七八行: 只需要 原n/8次  交叉填 
		for(int t=1; t<=n; t++) { 
		
		//交叉填 
			for(int i=m+1; i<=2*m; i++) { //控制行
				for(int j=m+1; j<=2*m; j++) { //j和t 和m一起控制列  
				//每天交叉填完两个m*m大小的方格,列就要往后移动两个m 
					a[i][j+(t-1)*m*2-m]=a[i-m][j+(t-1)*m*2];//左下角等于右上角的值
					a[i][j+(t-1)*m*2]=a[i-m][j+(t-1)*m*2-m];//右下角等于左上角的值
				}
			}
/*					 
			i-m  j+(t-1)*m*2-m  ...  i-m  j+(t-1)*m*2
            ...                       ...
             
			...                       ...
待填的部份:	i    j+(t-1)*m*2-m       i    j+(t-1)*m*2
			... 
			i+m+1 
*/    
//输出日程表方便理解
			for(int i=1; i<=8; i++) {
				for(int j=1; j<=8; j++) {
					cout<<a[i][j]<<" ";
				}
				cout<<endl;
			}
			cout<<"---------------"<<endl;
			
		}
		m*=2;//下一轮交叉填的大小变为原来的两倍
		//1*1 -> 2*2 -> 4*4 
	}
}
 
int main() {
	Table(3);//调用函数 
	//输出日程表 
	for(int i=1; i<=8; i++) {
		for(int j=1; j<=8; j++) {
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MORE_77

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值