基于分治法的循环日程表算法

基于分治法的循环日程表算法

一.题目

设有 n=2^k 个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:
(1) 每个选手必须与其他 n-1 个选手各赛一次。
(2) 每个选手一天只能赛一次。
(3) 循环赛一共进行 n-1 天。 按此要求可将比赛日程表设计成有 n 行和 n-1 列的日程表。在表中的第 i 行和第 j 列处填入 第 i 个选手在第 j 天所遇到的选手。

二.思路

看到问题的第一反应是在求全排列的满足条件的子集,但是全排列较多且选择适当的子集也比较困难,该问题有很多解,但求出其中的一种即可,那么就可以找最简单的一种进行求解。

n 为偶数,该问题可被分割为偶数个与原问题相似的子问题且最简单的子问题可轻易被求解,满足分治法的条件。即将大问题拆解为相似的可解决的小问题进行解决, 最后再将小问题的解进行合并,最终得到大问题的解。 可采用递归和非递归两种方法实现分治法思想。

最简单的子问题为 k=1 时,即两个运动员位置交错即可,由此合并,k=2 时是交错的两个的组合再交错,…由此推出 k=n 时解法 可将整个部分分为四个大块,求解出左上角,进而求解出其他三个部分,而左上角又由进一 步的递归求解…如此往复。

可采用递归和非递归两种方法实现分治法思想。
在这里插入图片描述
在这里插入图片描述

三.伪代码

1.递归

CIRCULAR_CALENDER(A,n)//A[1..n][1..n]
  IF(n==1)
      RETURN A[1][1]=1
  ELSE
      CIRCULAR_CALENDER(A,n/2)
      COPY_CALENDER(A,n)
      
COPY(A,n)

m=n/2

FOR i = 1 TO m
      FOR j = 1 TO m 
          A[i+m][j+m]=A[i][j] //左上复制到右下
          A[i][j+m]=A[i][j]+2^(m-1) //计算左下   (A[i][j+m]=A[i][j]+m也行 )
          A[i+m][j]= A[i][j+m]//左下复制到右上

2.非递归

CIRCULAR_CALENDER(A,n)
  A[1][1]=1
  k=logn

  FOR j = 1 TO k
     m = POWER(2, j) //从1开始边长每次扩大两倍
     COPY_CALENDER(A, m)//复制

四.代码实现

这里采用递归法实现

#include <iostream>

using namespace std;
void copy_calender(int n,int **A){

      int m=n/2;

      for(int i=1;i<=m;i++){

        for(int j=1;j<=m;j++){

            A[i+m][j+m]=A[i][j];
            A[i+m][j]=A[i][j]+m;
            A[i][j+m]=A[i+m][j];

        }
      }


}
int** circular_calender(int n,int **A)
{

    if(n==1)
    {
        A[1][1]=1;
        return A;

    }
    else
    {
      circular_calender(n/2,A);
      copy_calender(n,A);


    }


    return A;



}

int power(int k)
{

    int n=1;

    while(k--)
    {
        n*=2;
    }
    return n;

}
void print(int **A,int n){

  
  for(int i=1;i<=n;i++){
    for(int j=1;j<=n;j++){
        printf("%5d",A[i][j]);
    }
    cout<<endl;
  }

}

int main()
{
    int k=4;
    //cin>>k;

    int n=power(k);


    int **A=new int*[n+1];
    for(int i=0;i<=n;i++){
        A[i]=new int[n+1];
    }

    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            A[i][j]=0;
        }
    }



    circular_calender(n,A);
    print(A,n);



    return 0;
}

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值