题目背景
NOIp2015 提高组 Day1T1
题目描述
幻方是一种很神奇的 N×N 矩阵:它由数字 1,2,3,⋯⋯,N×N 构成,且每行、每列及两条对角线上的数字之和都相同。
当 N 为奇数时,我们可以通过下方法构建一个幻方:
首先将 11 写在第一行的中间。
之后,按如下方式从小到大依次填写每个数K (K=2,3,⋯,N×N) :
- 若(K−1) 在第一行但不在最后一列,则将 K 填在最后一行,(K−1) 所在列的右一列;
- 若 (K−1) 在最后一列但不在第一行,则将 K 填在第一列, (K−1) 所在行的上一行;
- 若 (K−1) 在第一行最后一列,则将 K 填在 (K−1) 的正下方;
- 若 (K−1) 既不在第一行,也不在最后一列,如果 (K−1) 的右上方还未填数,则将 K 填在 (K−1) 的右上方,否则将 K 填在 (K−1) 的正下方。
现给定 N ,请按上述方法构造N×N 的幻方。
输入格式
一个正整数 N,即幻方的大小。
输出格式
共 N 行,每行 N 个整数,即按上述方法构造出的 N×N 的幻方,相邻两个整数之间用单空格隔开。
输入输出样例
输入 #1复制
3
输出 #1复制
8 1 6 3 5 7 4 9 2
说明/提示
对于 100% 的数据,对于全部数据, 1≤N≤39 且 N 为奇数。
思路
因为:
- 若(K−1) 在第一行但不在最后一列,则将 K 填在最后一行,(K−1) 所在列的右一列;
- 若 (K−1) 在最后一列但不在第一行,则将 K 填在第一列, (K−1) 所在行的上一行;
- 若 (K−1) 在第一行最后一列,则将 K 填在 (K−1) 的正下方;
- 若 (K−1) 既不在第一行,也不在最后一列,如果 (K−1) 的右上方还未填数,则将 K 填在 (K−1) 的右上方,否则将 K 填在 (K−1) 的正下方。
所以:
当前一个数的坐标符合以上任意一条件时,对当前数的x和y坐标进行更改并填数,继续递归直到n*n数组被填完.
需要注意的是:要先找好第一个数的位置,第一个数在第一行的最中间,由于n是奇数,所以它的x坐标为1,y坐标为n/2+1.
AC代码
#include <bits/stdc++.h>
using namespace std;
int a[50][50],n; //初始化定义
void dfs(int num,int x,int y)
{
if(num==n*n+1) return; //递归边界
if(x==1 && y!=n)
{
x=n;
y++;
}
else if(x!=1 && y==n)
{
x--;
y=1;
}
else if(x==1 && y==n)
{
x++;
}
else if(x!=1 && y!=n)
{
if(a[x-1][y+1]==0)
{
x--;
y++;
}
else
{
x++;
}
}
a[x][y]=num;
dfs(num+1,x,y);
}
int main()
{
cin>>n;
int y=n/2+1;
int x=1;
a[x][y]=1; //寻找第一个数所放的位置
dfs(2,x,y);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cout<<a[i][j]<<" "; //打印幻方
}
cout<<endl;
}
return 0;
}