循环日程安排问题

【问题描述】

设有n = 2^k 个选手要进行网球循环赛,设计一个满足以下要求的比赛日程表:

        1、每个选手必须与其它n-1个选手各赛一次

        2、每个选手一天只能赛一次

        3、循环赛在n-1天之内结束

【问题求解】

        按照问题要求可将比赛日程表设计成一个n行n-1列的二维表,其中第i行,第j列表示第i个选手在第j天比赛的选手

        假设n位选手被顺序编号为1, 2, ... , n ( 2^k) ,当 k = 1,2,3是比赛日程表如下图所示,其中第一列递增,取值为1~n,表示1~n各个选手,这样比赛日程表变成了一个n行n列的二维表

        从图中可以看出规律,k = 1只有两个选手比赛安排十分简单,而k = 2时i可以基于k = 1的结果进行安排,k = 3时可以基于k = 2的结果进行安排。

        仔细观察k = 3的比赛日程表,右下角的值与左上角的值相等,左下角的值与右上角的值相等(4行4列),并且k = 3的左上角(4行4列)的值等于k = 2(即4个选手)的比赛日程表,左下角的值等于左上角的值加上数字4(2^(k-1)),因此,可以采用分治策略将所有的选手分为两半,2^k个选手的比赛日程表就可以通过为2^(k-1)个选手设计的比赛日程来决定。将n = 2 ^ k 问题划分为4个部分,

    (1)、左上角:左上角为2^(k-1)个选手在前半程的比赛日程(k=1时直接给出,否则上一轮求出的            就是2^(k-1)个选手的比赛日程)
    (2)、左下角:左下角为另2^(k-1)个选手在前半程的比赛日程,由左上角加2^(k-1)得到,例如2个选          手比赛, 左下角由左上角直接加2(2^(k-1))得到,2^3个选手比赛,左下角由左上角直接加                 4(2*-1)得到。
    (3)、右上角:将左下角直接复制到右上角得到另2^(k-1)个选手在后半程的比赛日程。
    (4)、右下角:将左上角直接复制到右下角得到2^(k-1)个选手在后半程的比赛日程。

实现代码如下所示:

#include "iostream"

using namespace std;
const int MAX = 101;
int k;
//求解结果表示
int ans[MAX][MAX];      //存放比赛日程表(行,列下标为0的元素不用)
/**
 * 输入k,共有n = 2^k个选手,ans为返回结果表示安排好的日程
 * @param k
 */
void plan(int k) {
    int n = 2;
    //n从2^1 = 2开始
    ans[1][1] = 1;
    ans[1][2] = 2;
    ans[2][1] = 2;
    ans[2][2] = 1;
    for (int i = 0; i < k; ++i) {
        int temp = n;       //temp = 2 依次处理2^2,2^3,...2^k个选手
        n *= 2;             //n = 4
        //填充左下角元素,左下角元素等于左上角元素+temp
        for (int p = temp + 1; p <= n; p++) {
            for (int q = 1; q <= temp; q++) {
                ans[p][q] = ans[p - temp][q] + temp;
            }
        }
        //填充右上角元素,右上角元素与左下角元素相同
        for (int p = 1; p <= temp; p++) {
            for (int q = temp + 1; q <= n; q++) {
                ans[p][q] = ans[p + temp][q - temp];
            }
        }
        //填充右下角元素,右下角元素与左上角元素相同
        for (int p = temp + 1; p <= n; p++) {
            for (int q = temp + 1; q <= n; q++) {
                ans[p][q] = ans[p - temp][q - temp];
            }
        }

    }
}

int main() {
    k = 3;
    int n = 1 << k;
    plan(k);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++)
            cout << ans[i][j] << " ";
        cout << endl;
    }

}

运行结果如下所示:

修改k的值为4可以发现日程表如下所示:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值