循环赛日程表--分治--递推&递归

设有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个选手的比赛日程表如下图:

递归法

#include<iostream>
#include<vector>
#include<iterator>
#include<algorithm>
using namespace std;
 
/*循环赛日程表(递归法)*/
 
void Copy(int **map,int sr,int sl,int dr,int dl,int k){
	for (int i = 0; i < k; i++){
		for (int j = 0; j < k; j++){
			map[dr+i][dl+j] = map[sr+i][sl+j];
		}
	}
}
 
void Table(int **map,int r,int l,int k){
	if(1==k)return;
	//填充左上角
	Table(map,r,l,k/2);
	//填充右上角
	Table(map,r,l+(k/2),k/2);
	//从左上角拷贝到右下角
	Copy(map,r,l,r+k/2,l+k/2,k/2);
	//从右上角拷贝到左下角
	Copy(map,r,l+k/2,r+k/2,l,k/2);
}
 
int main(){
	int k;
	cin>>k;//输入运动员的人数
	int ** p =new int*[k];
	//赛程表初始化
	for (int i = 0; i < k; i++){
		p[i] = new int[k];
		p[0][i] = i+1;
		p[i][0] = i+1;
	}
	Table(p,0,0,k);
	cout<<"cycle competetion schedule:"<<endl;
	for (int i = 0; i < k; i++){
		copy(p[i],p[i]+k,ostream_iterator<int>(cout," "));
		cout<<endl;
	}
	return 0;
}

递推 + 递归

#include<iostream>
#include<vector>
#include<iterator>
#include<algorithm>
#include<stdio.h>
#include<math.h>
using namespace std;

int a[100][100];/*循环赛日程表的全局变量*/

/*是一个copy函数,目的是把以r为长度的正方形范围从点from到点to*/
void copy(int tox,int toy,int fromx,int fromy,int r){
    for(int i=0;i<r;i++)
      for(int j=0;j<r;j++)
        a[tox+i][toy+j]=a[fromx+i][fromy+j];
}

/*递归算法*/
void Table_recursion(int a[][100],int r,int l,int k){
    if(k==1)                       /*如果就一个当然就直接返回啦*/
        return;
    Table_recursion(a,r,l,k/2);        /*填充左上角*/
    Table_recursion(a,r+(k/2),l,k/2);  /*填充左下角*/
    copy(r+k/2,l+k/2,r,l,k/2);     /*从左上角拷贝到右下角*/
    copy(r,l+k/2,r+k/2,l,k/2);     /*从左下角拷贝到右上角*/
}

/*非递归算法*/
void Table_recursive(int k){
    int n=pow(2,k);
    for(int i=0;i<n;i++) /*第一列初始化*/
      a[i][0]=i+1;
    for(int r=2;r<=n;r*=2) /*如果要填充整个表,要有k趟,每一趟的步长为2^i(i=1,2,...k)*/
      for(int i=0;i<n;i+=r){ /*针对某一趟,假设这趟步长为r,则需要复制正方形的边长为r的一半*/
        int w=r/2;
        copy(i+w,w,i,0,w);
        copy(i,w,i+w,0,w);
      }
}

void print(int a[][100],int n){/*输出整个表*/
    for(int i=0;i<n;i++)
      for(int j=0;j<n;j++){
        cout<<a[i][j]<<" ";
        if(j==n-1)
            puts("");
      }
}
 
int main(){

    int k,n;
    cout<<"input the power of 2: k = ";
    cin>>k;

    n=pow(2,k);
    cout<<"this is recursive algorithm"<<endl;
    Table_recursive(k);
    print(a,n);
    for(int i=0;i<n;i++)
      a[i][0]=i+1;
    puts("");
    cout<<"this is recursion algorithm"<<endl;
    Table_recursion(a,0,0,n);
    print(a,n);
}

递推2

#include<stdio.h>
#include<math.h>

void gametable(int k){       
    int a[100][100];
    int n,temp,i,j,t;
    n=2;//k=0两个参赛选手日程可以直接求得
    a[1][1]=1;a[1][2]=2;
    a[2][1]=2;a[2][2]=1;

    //迭代处理,依次处理2^n....2^k个选手的比赛日程
    for(t=1;t<k;t++){
        temp=n;n=n*2;//填左下角元素
        for(i=temp+1;i<=n;i++)
            for(j=1;j<=temp;j++)
                a[i][j]=a[i-temp][j]+temp;//左下角和左上角元素的对应关系
        for(i=1;i<=temp;i++)//将左下角元素抄到右上角
            for(j=temp+1;j<=n;j++)
                a[i][j]=a[i+temp][(j+temp)%n];
        for(i=temp+1;i<=n;i++)//将左上角元素抄到右下角
            for(j=temp+1;j<=n;j++)
                a[i][j]=a[i-temp][j-temp];
     
    }
    printf("number:%d\n\n",n);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){
            printf("%d ",a[i][j]);
                if(j==n)  printf("\n");       
        }
}

int main(){
    int k;
    printf("how many people :input K(K>0):\n");
    scanf("%d",&k);
    if(k!=0)  gametable(k);
    
    return 0;
}

其他递归解法
https://blog.csdn.net/qq_32919451/article/details/80634671

不怕丢人再贴一个自己第一次写的,还没debug出来QAQ

#include <stdio.h>

int f(int i,int j){
	int a[9][9]={0};
	int k;
	for(k=1;k<=8;k++){
		a[k][1]=k;
		if(k%2==1) a[k][2]=k+1;
				   else a[k][2]=k-1;
	}
	while(1){
		if(1<=i<=8 && 1<=j<=2) return a[i][j];
		if(5<=i<=8 && 5<=j<=8) return f(i-4,j-4);
		if(1<=i<=4 && 5<=j<=8) return f(i+4,j-4);
		if((3<=i<=4 && 3<=j<=4)||(7<=i<=8 && 3<=i<=4))
			return f(i-2,j-2);
		if((1<=i<=2 && 3<=j<=4)||(5<=i<=6 && 3<=j<=4))
			return f(i+2,j-2);
		//if(1<=i<=8 && 1<=j<=2) return a[i][j];
	}
}

int main(){
	int m,n;
	scanf("%d %d",&m,&n);
	printf("%d",f(m,n));
	return 0;
}

反思分析:

  • 不具通用性,规律总结不到位,抄表时复制子表阶数是和输入值k相关的
  • 循环赛的表只有队员数为2的k次幂才能画出
  • 本题递推与递归最明显的不同:递推以for循环为主,递归则是自己调用自己
发布了49 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/knaha/article/details/95205477