用分治法解决循环赛日程表

1.分治法的基本思想

将一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同,递归的解决这些子问题,然后将各子问题的解合并得到原问题的解。

2.算法设计的一般模式:

divide-and-conquer(p)

     if(|p|<=n0)  adhoc(p);//当问题的规模不超过n0时,问题已容易解出,不再分解,adhoc(P)用于直接解规模最小的问题

    divide p into smaller subinstances P1,P2,...,Pk;

    for(i=1;i<k;i++)

    yi=divide-and-conquer(Pi);

    return merge(y1,..,yk);//合并子算法

}

3.分治法的具体应用-循环赛日程表

3.1问题描述:

设有n=2^k个运动员要进行网球循环赛。要满足以下要求:
(1)每个选手必须与其他n-1个选手各赛一次
(2)每个选手一天只能赛一次
(3)循环赛一共进行n-1天
 
输入
一个小于等于7的正整数k
 
输出
循环赛日程表(1到2的k次方个人)
 
输入样例
3
 
输出样例
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
 3.2问题分析
按分治策略,可以将所有选手分为两半,n个选手的比赛日程表就可以通过为n/2个选手设计的比赛日程表来决定。递归地用这种一分为二的策略对选手进行分割,直到只剩下两个选手时,日程表的制定就很简单了。两个选手进行比赛就可以了。
3.3代码实现
#include <iostream>
#include <cstdio>
using namespace std;
int a[10000][10000];//定义数组表示选手在第i个选手在第j天遇到的选手
void table(int k, int n)
{
    for(int i = 1; i <= n; i ++)
    {
        a[1][i] = i;
    }
    int m = 1;                                                   
    for(int s = 1; s <= k; s++)//将问题分成了几个部分
    {
        n/=2;//每次都将问题一分为二
        for(int t = 1; t <= n; t++)//再对每个部分进行划分                                                     
            for(int i = m+1; i <= 2*m; i++)
                for(int j = m+1; j <= 2*m; j++)
                {
                    a[i][j+(t-1)*m*2] = a[i-m][j+(t-1)*m*2-m];//将左上角小块中的所有数字按其相对位置抄到右下角
                    a[i][j+(t-1)*m*2-m] = a[i-m][j+(t-1)*m*2];//将左下角小块中的所有数字按其相对位置抄到右上角
                    //printf("i = %d\t j+(t-1)*m*2 = %d\t j+(t-1)*m*2-m = %d\t, i-m=%d\n", i, j+(t-1)*m*2, j+(t-1)*m*2-m, i-m);
                }
        m *= 2;//逐步扩大复制块,递归地解决问题                                                
    }
}
int main()
{
    int k;
    cin >> k;

    int n = 1;
    for(int i = 1; i <= k; i++)
        n *= 2;
    table(k, n);

    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= n; j ++)
        {
            printf("%d%c", a[i][j], j!=n?' ':'\n');
        }
    }
    return 0;
}

      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值