1243 循环赛日程表

时限:1000ms 内存限制:10000K 总时限:3000ms

描述
用分治算法生成循环赛日程表(1到2的n次方个人)

输入
一个整数n

输出
循环赛日程表(1到2的n次方个人)

输入样例
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
分析:
采用分治算法,把一个问题分为四个子问题,即一个表格平均划分为四个表格,其中右下角与左上角完全相同,右上角与左下角完全相同,所以在该问题中,只用算出左上角和左下角的表或右上角和右下角的表再复制即可。

#include <iostream>

using namespace std;
int n;
int a[10000][10000];
void table(int k,int m)
{
    int i,j;
    if(m==2)
    {
        a[k-1][0]=k;      //第一行第一列元素为1
        a[k][0]=k+1;      //第二行第一列元素为2
    }
    else
    {
        table(k,m/2);     //子问题:当前表格左上角和左下角的表格
        table(k+m/2,m/2);
    }
    for(i=k-1;i<k-1+m/2;i++)   //右上的表格中元素与左下的相等
        for(j=m/2;j<m;j++)
        a[i][j]=a[i+m/2][j-m/2];
    for(i=k-1+m/2;i<k-1+m;i++)   //右下的表格中元素与左上的相等
        for(j=m/2;j<m;j++)
        a[i][j]=a[i-m/2][j-m/2];
}
int main()
{
    int i,j,m=1;
    cin>>n;
    for(i=0;i<n;i++)
        m*=2;
    table(1,m);                 //从当前表的第1个元素开始,当前的表共有m行m列
    for(i=0;i<m;i++)
    {
      for(j=0;j<m-1;j++)         //前m-1列每一个元素后面都有空格
        cout<<a[i][j]<<" ";
      cout<<a[i][j];            //最后一列单独写,因为最后一列元素后面没有空格
      cout<<endl;
    }
    return 0;
}

需要注意的是,在复制的过程中,行和列for循环的条件是不同的

for(i=k-1;i<k-1+m/2;i++)  
        for(j=m/2;j<m;j++)
        a[i][j]=a[i+m/2][j-m/2];

如左下复制给右上时,行是从第k-1个开始,而列始终从m/2开始即可
这是因为,在n=3的情况下
递归调用table的顺序是

table(1,4)
→table(1,2)
→table(3,2)

table(5,4)
→table(5,2)
→table(7,2)
然后m=2,给定第一列每个元素一个值
再复制给第二列
由此可以观察出,此递归调用的执行顺序是,先给第一列每一个元素一个初值,然后大体上按从左到右的顺序依次给每一列的元素赋值,而每一列元素获得值的具体实现又是按照左上=右下,左下=右上,对角线的方法得到的
故行的下标跟k值息息相关,而列按照大体上从左到右的顺序即可,与k无关

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值