•设有n个运动员要进行网球循环赛。设计一个满足下列条件的比赛日程表:
–每个选手必须与其他n-1个选手各赛一次;
–每个选手一天只能赛一次;
–当n是偶数时,循环赛进行n-1天。
–当n是奇数时,循环赛进行n天。
算法分析:
按分治策略,将所有的选手分为两半,n个选手的比赛日程表就可以通过为n/2个选手设计的比赛日程表来决定。递归地用对选手进行分割,直到只剩下2个选手时,比赛日程表的制定就变得很简单。这时只要让这2个选手进行比赛就可以了。
该算法的核心问题是合并问题,因为把一个数分成2半,可能得到2种情况:①等分;②一组比另一组多一人;在这里我们巧妙的设计了一下,当一组比另一组多一人时,我们假设了一个虚拟选手,让他参与匹配,最后再把出现虚拟对手的位置置为0,表示这个位置对应的选手当天没有比赛:
/*
* 循环赛日程表.cpp
* Author: Au酱
* Date:2013-04-12
*/
#include <iostream>
#include <iomanip>
#include <cmath>
#include <conio.h>
using namespace std;
const int MAX_NUM = 100;
int A[MAX_NUM+2][MAX_NUM+2];
/* 合并子问题 */
void merge(int n)
{
/*
* n 为偶数时,比赛 n - 1 天
* n 为奇数时,比赛 n 天
*/
int days = n % 2 == 0 ? n - 1 : n;
/*
* 中间值,若n为奇数,则使 m = (n / 2) + 1,
* 即,前半部分不小于后半部分
*/
int m = (int)ceil(n / 2.0);
int passd_days = m % 2 == 0 ? m - 1 : m; /* 已经安排的天数 */
/*
* 通过前 n/2 的比赛安排,构造后n/2的比赛安排
* 如果 n 为奇数,则会产生一个虚拟选手
*/
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= passd_days; j++)
{
if (A[i][j] != 0) /* 如果 i 号在第 j 天有对手 */
{
/*
* 那么,(i + m) 号在第 j 天的对手为 i号的
* 对手往后数 m 号
*/
A[i + m][j] = A[i][j] + m;
}
else /* 如果 i 号在第 j 天没有对手*/
{
/*
* 那么就让 i 号和 (i + m)号互为对手
*/
A[i + m][j] = i;
A[i][j] = i + m;
}
}
}
int add_one = 0; /* 稍后说明 */
if (A[1][passd_days] == m + 1)
add_one = 1;
for (int i = 1; i <= m; i++)
{
for (int j = passd_days + 1, count = 0; j <= days; j++, count++)
{
/*
* i 号在第 j 天的对手,通过枚举后半部分的值
* 来进行匹配
*/
int r_value = (count + (i - 1) + add_one) % m + m + 1;
A[i][j] = r_value;
A[r_value][j] = i;
}
}
if ( n % 2 == 1) /* 如果 n 为奇数,消除虚拟的选手 */
{
for (int i = 1; i <= 2 * m; i++)
{
for (int j = 1; j <= days; j++)
if (A[i][j] == n + 1)
A[i][j] = 0; /* A[i][j] = 0 ,表示 i 号选手在第 j 天没有比赛 */
}
}
}
/* 分治求解循环赛问题 */
void tournament(int n)
{
if (n <= 1)
return;
else if (n == 2) /* 2 个选手 */
{
A[1][1] = 2;
A[2][1] = 1;
}
else
{
tournament((int)ceil(n / 2.0));
merge(n);
}
}
/* 打印循环赛日程表 */
void show_result(int n)
{
cout << " " << n << "人循环赛" << endl;
int days;
if (n % 2 == 0)
days = n - 1;
else
days = n;
cout.flags(ios::left);
cout << setw(8) << "";
for (int i = 1; i <= n; i++)
cout << i << setw(4) << "号";
cout << endl;
cout.flags(ios::left);
for (int j = 1; j <= days; j++)
{
cout << "第 " << setw(2) << j << setw(3) << "天";
for (int i = 1; i <= n; i++)
{
cout << setw(5) << A[i][j];
}
cout << endl;
}
cout << endl;
}
int main()
{
cout << "请输入参赛人数(小于100):";
int num;
cin >> num;
tournament(num);
show_result(num);
cout << "Hit any key to close this window.";
_getch();
}