时限: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无关