【分治法】循环赛事表

时间:2022-06-20 20:30:28

题意:

2k 个运动员进行网球循环赛,设计赛事表使得:

  • 每个选手与其他n-1个选手各赛一次
  • 每个选手每天只能赛一次
  • 循环赛在n-1天内结束

日程表第i行第j列表示第i个选手在第j天遇到的选手

分析:

考虑k=3,n=8的情况,利用分治思想,将所有选手不停的分为两组,最终转化为只剩两个人进行比赛,再根据两个人的比赛安排得到整体赛事表。

  • 先列出要比赛的8位选手
    for(int i = 1; i <= 8; i++) a[1][i] = i;
  • 考虑表格前两行,两个人一组,将表格分成n/2块,将每一块的左上角元素抄到右下角,右上角元素抄到左下角
  • 继续考虑前四行,四个人一组,将表格分成n/4块,同理将每一块的左上角元素抄到右下角,右上角元素抄到左下角
  • 最后考虑前八行,八个人一组,将表格看成一个整体,同理将左上角的元素抄到右下角,右上角元素抄到左下角
  • 最终获得日程表
1 2 3 4 5 6 7 8
2 1 4 3 6 5 8 7
3 4 1 2 7 8 5 6
4 3 2 1 8 7 6 5
5 6 7 8 1 2 3 4
6 5 8 7 2 1 4 3
7 8 5 6 3 4 1 2
8 7 6 5 4 3 2 1

代码:

#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
const int maxn = 10005;
int a[maxn][maxn];
int main (void)
{
    int k, m = 1;
    cin>>k;
    int t = pow(2,k);
    int n = t;

    //第一行
    for(int i = 1; i <= n; i++)  a[1][i] = i;

    //依次分块,并将每块的左上角元素抄到右下角,右上角抄到左下角
    for(int i = 1; i <= k; i++){
        n /= 2;
        for(int p = 1; p <= n; p++){
            for(int j = 1; j <= m; j++){
                for(int k = 1; k <= m; k++){
                    a[j+m][k+2*m*(p-1)] = a[j][m+k+2*m*(p-1)];
                    a[j+m][m+k+2*m*(p-1)] = a[j][k+2*m*(p-1)];
                }
            }
        }
        m *= 2;
    }

    //打印表格
    for(int i = 1; i <= t; i++){
        for(int j = 1; j <= t; j++){
            cout<<a[i][j]<<' ';
        }
        cout<<endl;
    }
}