分治算法 —— 循环赛日程表

1. 问题描述:

设有n=2^k个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:
(1)每个选手必须与其他n-1个选手各赛一次;
(2)每个选手一天只能参赛一次;
(3)循环赛在n-1天内结束。
请按此要求将比赛日程表设计成有n行和n-1列的一个表。在表中的第i行,第j列处填入第i个选手在第j天所遇到的选手。其中1≤i≤n,1≤j≤n-1。8个选手的比赛日程表如下图:
分治算法 —— 循环赛日程表_第1张图片

2. 题解

此题如果能发现是分治的题目,那就解决了一半了。我们需要安排2^k支队伍,如果我们能根据2对2 ^ k-1支队伍的赛程表得到2 ^ k支队伍的赛程表,然后自顶向下,也就是说,我只需要安排两支队伍(幼儿园小盆友就能安排hhh)。
分治算法 —— 循环赛日程表_第2张图片
如上图所示,假如我们拿到了2对4支队伍的赛程表,如何安排8支队伍呢?1号代替对面的5号和6,7,8比赛(反之亦然),2代替6,3代替7,4代替8,这里的代替是双向的。这样,每支队伍就赛了6场,接下来只需要再增加一天,让刚才相互替换的两支队伍分别交锋即可完成。
分治算法 —— 循环赛日程表_第3张图片
分治算法 —— 循环赛日程表_第4张图片
第一次拿到这个问题很可能没有思路,这种情况建议打表。当然,如果提前知道这是个分治问题,那问题就容易多了。但是我们假设不知道正解,我们以 k = 3为例,即安排8支队伍的比赛交锋。可以观察一下,不难发现其中有着明显的规律:
分治算法 —— 循环赛日程表_第5张图片
相信接下来无需我多说小伙伴们也能懂了,2 ^ k-1也存在着同样的规律,8支队伍A,B矩阵差为4,4支队伍差为2,以此类推 ··· 其中的规律其实就是分治算法的思路,只不过第一次学习分治很难直接想到简单的策略~

3. 代码:

//
// Created by 23011 on 28/3/2022.
//

#include
using namespace std;

const int maxn = 10000;
int a[maxn][maxn];

inline void dfs(int n,int k)
{
    if(n == 2)
    {
        a[k][0] = k+1;
        a[k][1] = k+2;
        a[k+1][0] = k+2;
        a[k+1][1] = k+1;
    }
    else
    {
        dfs(n/2,k);
        dfs(n/2,k+n/2);
        for(int i = k; i < k+n/2; i++)
        {
            for(int j = n/2; j < n; j++) a[i][j] = a[i+n/2][j-n/2];
        }
        for(int i = k+n/2; i < k+n; i++)
        {
            for(int j = n/2; j < n; j++) a[i][j] = a[i-n/2][j-n/2];
        }
    }
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        dfs(n,0);
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < n; j++) printf("%d ",a[i][j]);
            printf("\n");
        }
    }
    return 0;
}

你可能感兴趣的:(算法,算法)