Description
n = 2^k个选手(编号1 ∼ n)进行循环赛,日程表要求:
每个选手必须与其他𝑛−1个选手各赛一次;
每个选手一天只能赛一次;
循环赛一共进行𝑛−1天.
设计一个𝑛行𝑛−1列的表,第𝑖行第𝑗列填入第𝑖个选手第𝑗天的对手.
Input
多组测试数据,每组给出选手个数n. 其中n ≤ 32
Output
给出n行n − 1列的日程表.
Sample Input
8
Sample Output
2 3 4 5 6 7 8
1 4 3 6 5 8 7
4 1 2 7 8 5 6
3 2 1 8 7 6 5
6 7 8 1 2 3 4
5 8 7 2 1 4 3
8 5 6 3 4 1 2
7 6 5 4 3 2 1
看完这道题,心想:这不就是数独吗???
第一思路是想使用暴力搜索枚举每个选手每天的情况,直到找到一种答案就输出然后结束程序即可
具体细节可看以下代码注释
#include<iostream> using namespace std; const int MAX=100; int arr[MAX][MAX]; int visited[MAX][MAX];//定义为visited[i][j]选手i是否已经和选手j比赛过 int n; void dfs(int x,int y){//第x个选手第y天 if(y>=1&&y<=n-1){//天数未越界 if(x>=1&&x<=n){//选手数量未越界 if(!arr[x][y]){//当天未安排对手 for(int i=1;i<=n;i++){ if(!visited[x][i]&&!arr[i][y]){//未与i打过且他当天有空 arr[x][y]=i;//x的当天对手是i arr[i][y]=x;//对手的当天对手是x visited[x][i]=true; visited[i][x]=true; dfs(x+1,y); } } }else{ dfs(x+1,y);//下一个选手 } }else{ dfs(1,y+1); //下一天 } }else{ for(int i=1;i<=n;i++){ for(int j=1;j<=n-1;j++){ if(j==1){ cout<<arr[i][j]; }else{ cout<<" "<<arr[i][j]; } } cout<<endl; } exit(0); } } signed main(){ cin>>n; for(int i=1;i<=n;i++){ visited[i][i]=true;//将自己与自己比赛禁止 } dfs(1,1); return 0; }
思路2:分治
为方便理解,我们将日程表扩为正方形
按分治策略,我们可以将所有的选手分为两半,则n个选手的比赛日程表可以通过n/2个选手的比赛日程表来决定。递归地用这种一分为二的策略对选手进行划分,直到只剩下两个选手时,比赛日程表的制定就变得很简单。这时只要让这两个选手进行比赛就可以了。
例如上图,所列出的正方形表是8个选手的比赛日程表。其中左上角与左下角的两小块分别为选手1至选手4和选手5至选手8前3天的比赛日程。因为前四位选手在前四天已经与其对手打过了,所以他们的对手不可能在出现在前四天,据此,将左上角小块中的所有数字按其相对位置抄到右下角,同理后四位选手在前四天的对手只可能出现在前四位选手的后四天比赛中,所以又将左下角小块中的所有数字按其相对位置抄到右上角,这样我们就分别安排好了选手1至选手4和选手5至选手8在后4天的比赛日程。
根据该思想我们就可以写代码了
#include<iostream> #include<cmath> using namespace std; const int MAX=100; int n; int arr[MAX][MAX]; int getCount(int n){ int count=0; while(n>1){ n/=2; count++; } return count; } int main(){ cin>>n; int len=n; for(int i=1;i<=n;i++)arr[1][i]=i;//初始化(扩为正方形) int count=getCount(n);//得到分治次数 int m=1; for(int t=1;t<=count;t++){ n/=2;//分成几部分 for(int k=1;k<=n;k++){//对每一部分进行计算 for(int i=m+1;i<=2*m;i++){//确定行 for(int j=m+1;j<=2*m;j++){ arr[i][j + (k-1)*m*2] = arr[i - m][j + (k-1)*m*2 - m]; //右下角等左于上角的值 arr[i][j + (k-1)*m*2 - m] = arr[i - m][j + (k-1)*m*2]; //左下角等于右上角的值 } } } m*=2;//下一部分 } for(int i=1;i<=len;i++){ for(int j=2;j<=len;j++){ if(j==2){ cout<<arr[i][j]; }else{ cout<<" "<<arr[i][j]; } } cout<<endl; } return 0; }