【循环赛日程表】“递归与分治策略”——《算法设计与分析(第五版)》


一、算法要求

设计一个满足以下要求的比赛日程表:
(1) 每个选手必须与其他n-1个选手各赛一次;
(2) 每个选手一天只能赛一次;
(3) 循环赛一共进行n-1天。

传统方法:将比赛日程表设计为:n行 n-1列的表(i,j)表示:第i个选手在第j 天 遇到的选手。

1. 思路

分治法不仅可以用来设计算法,在其他方面也有广泛应用,如用分治思想来设计电路、构造数学证明等。

举例说明。
	设有n=2个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:
		①每个选手必须与其他n-1个选手各赛一次;
		②每个选手一天只能赛一次;
		③循环赛一共进行n-1天。
	按此要求可将比赛日程表设计成有n行和n-1列的表。在表中第i行和第j列处填入第i个选手在第j天所遇到的选手。
	按分治策略,可以将所有选手对分为两半,n个选手的比赛日程表就可以通过为n/2个选手设计的比赛日程表来决定。
	递归地用这种一分为二的策略对选手进行分割,直到只剩下两个选手时,比赛日程表的制定就变得简单了。
	这时只要让这两个选手进行比赛就可以了。

2. 示例

在这里插入图片描述


二、完整代码

1. 主文件

main.cpp:

// Project1_2: 循环赛日程表

#include<iostream>    
#include<iomanip>
using namespace std;

void Print(int** a, int n){
	for (int i = 1; i <= n; i++){
		for (int j = 1; j <= n; j++){
			cout << "|" << setw(2) << a[i][j];
		}
		cout <<"|" << endl;
	}
}

void Schedule(int k, int n, int** a)
{
	for (int i = 1; i <= n; i++)
		a[1][i] = i;//设置日程表第一行

	int m = 1;//每次填充时,起始填充位置
	for (int s = 1; s <= k; s++)
	{
		n /= 2;
		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++)//控制列
				{
					a[i][j + (t - 1) * m * 2] = a[i - m][j + (t - 1) * m * 2 - m];//右下角等于左上角的值
					a[i][j + (t - 1) * m * 2 - m] = a[i - m][j + (t - 1) * m * 2];//左下角等于右上角的值
				}

			}
		}
		m *= 2;
	}
}

int main() {
	int k,	// 参赛人数是2^k次方
		n;	// 参赛人数

	cout << "#Please enter the number of people (index ‘k’ with base 2): ";
	cin >> k;

	n = pow(2, k);

	int** a = new int* [n + 1];//动态分配二维数组a
	for (int i = 0; i < n + 1; i++) {
		a[i] = new int[n + 1];
	}

	Schedule(k, n, a);

	cout << "\n#The recurring event schedule is:" << endl;
	Print(a, n);
	//释放空间
	for (int i = 0; i < n + 1; i++) {
		delete[] a[i];
	}
	delete[] a;

	return 0;
}


2. 效果展示

输入:

参赛人数:2的指数k = 4;

输出:在这里插入图片描述


三、补充

分治法是一种一般性的算法设计技术,它将问题的实例划分为若干个较小的实例(最好用有相同的规模),对这些小的问题求解,然后合并这些解,得到原是问题的解。
分治法的时间效率满足:T(n)=aT(n/b)+f(n)

分治法基本思想和基本步骤:

divide-and-conquer(P)
  {
    if ( | P | <= n0) adhoc(P);   //解决小规模的问题
    divide P into smaller subinstances P1,P2,...,Pk;//分解问题
    for (i=1,i<=k,i++)
      yi=divide-and-conquer(Pi);  //递归的解各子问题
    return merge(y1,...,yk);  //将各子问题的解合并为原问题的解
  }   

在用分治法设计算法时,最好使子问题的规模大致相同。即将一个问题分成大小相等的k个子问题的处理方法是行之有效的。
这种使子问题规模大致相等的做法是出自一种平衡(balancing)子问题的思想,它几乎总是比子问题规模不等的做法要好。

文档供本人学习笔记使用,仅供参考。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

NI'CE'XIAN

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

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

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

打赏作者

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

抵扣说明:

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

余额充值