提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
本文主要是介绍洛谷P2615 [NOIP2015 提高组] 神奇的幻方的解法。思路是参考大佬LesterYu。蒟蒻的哭泣 。看大佬代码琢磨了一个小时,终于弄懂了,欣喜若狂,就说说我对大佬LesterYU美丽的公式。
x=(x-2+n)%n+1;
y=y%n+1;
下面请欣赏美丽的幻方
一题目描述&&思路
幻方是一种很神奇的 N∗N 矩阵:它由数字1,2,3,⋯⋯,N×N 构成,且每行、每列及两条对角线上的数字之和都相同。
当 N 为奇数时,我们可以通过下方法构建一个幻方:
首先将 1 写在第一行的中间。
之后,按如下方式从小到大依次填写每个数 K (K=2,3,⋯,N×N) :
若 (K-1)(K−1) 在第一行但不在最后一列,则将 KK 填在最后一行, (K-1)(K−1) 所在列的右一列;
若 (K-1)(K−1) 在最后一列但不在第一行,则将 KK 填在第一列, (K-1)(K−1) 所在行的上一行;
若 (K-1)(K−1) 在第一行最后一列,则将 KK 填在 (K-1)(K−1) 的正下方;
若 (K-1)(K−1) 既不在第一行,也不在最后一列,如果 (K-1)(K−1) 的右上方还未填数,则将 KK 填在 (K-1)(K−1) 的右上方,否则将 KK 填在 (K-1)(K−1) 的正下方。
现给定 N ,请按上述方法构造N×N 的幻方。
思路:通过分析构造奇数阶幻方的条件,我们可以知道从第一行中间开始,
之后若上一个数右上角没有数则把数放在上面,若有数则放在上一个数的下面。
1.根据题目在(1,2)填1
行/列 | 1 | 2 | 3 |
---|---|---|---|
1 | 1 | ||
2 | |||
3 |
2.填完1后看1的"右上角"即(3,3),没数,填2
行/列 | 1 | 2 | 3 |
---|---|---|---|
1 | 1 | ||
2 | |||
3 | 2 |
3.填完2后看2的"右上角"即(2,1),没数,填3
行/列 | 1 | 2 | 3 |
---|---|---|---|
1 | 1 | ||
2 | 3 | ||
3 | 2 |
4.填完3后看3的"右上角"即(1,2),有数,填4在(3,1)
行/列 | 1 | 2 | 3 |
---|---|---|---|
1 | 1 | ||
2 | 3 | ||
3 | 4 | 2 |
5.填完4后看4的"右上角"即(2,2),没数,填5
行/列 | 1 | 2 | 3 |
---|---|---|---|
1 | 1 | ||
2 | 3 | 5 | |
3 | 4 | 2 |
6.填完5后看5的"右上角"即(1,3),没数,填6
行/列 | 1 | 2 | 3 |
---|---|---|---|
1 | 1 | 6 | |
2 | 3 | 5 | |
3 | 4 | 2 |
7.填完6后看6的"右上角"即(1,3),有数,填7在(2,3)
行/列 | 1 | 2 | 3 |
---|---|---|---|
1 | 1 | 6 | |
2 | 3 | 5 | 7 |
3 | 4 | 2 |
8.填完7后看7的"右上角"即(1,1),没数,填8在(1,1)
行/列 | 1 | 2 | 3 |
---|---|---|---|
1 | 8 | 1 | 6 |
2 | 3 | 5 | 7 |
3 | 4 | 2 |
8.填完8后看8的"右上角"即(3,2),没数,填9在(3,2)
行/列 | 1 | 2 | 3 |
---|---|---|---|
1 | 8 | 1 | 6 |
2 | 3 | 5 | 7 |
3 | 4 | 9 | 2 |
二、代码实现
#include <stdio.h>
int main() {
int n;
scanf("%d", &n);
int a[40][40] = {0};
int x = 1, y = (n + 1) / 2; //x,y代表幻方的行和列
//创建幻方
for (int i = 1; i <= n * n; ++i) {
a[x][y] = i;
if (!a[(x - 2 + n) % n + 1][y % n + 1]) {
x = (x - 2 + n) % n + 1;
y = y % n + 1;
} else {
x = x % n + 1;
}
}
//输出
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
printf("%d ", a[i][j]);
}
printf("\n");
}
}
下面对幻方创建中关键代码分析
if (!a[(x - 2 + n) % n + 1][y % n + 1]) {
x = (x - 2 + n) % n + 1;
y = y % n + 1;
} else {
x = x % n + 1;
}
上面的代码即实现判断右上角有没有数,无则填在右上角,有则填在正下方
我们知道a[n1][n2],n1代表行,n2代表列.
但为什么用(x - 2 + n) % n + 1和y % n + 1来表示呢?
我们想右上角不就是在上一行在右一列吗。
我们先看行的表达式。
上一行?简单。x-1,不就行了吗?可惜计算机没有那么智能,x=1时,不会帮我们跳到第n行。
想啊想。哎,再加一个n不就行了吗!这样x-1+n就可以实现从第一行跳到n行了。
仔细想想,好像其他行又不行了。x=2,3,4…时,得到的行大于n行了。
聪明的一休绕绕头。哎,我们有%。我们%n就可以了。(x-1+n)%n
我们又又又发现>1行的是可以了,但x=1时又失败了。
😫要疯啦!!!
卡了半个小时,我又滚去观摩大佬的原式了。
最后仿照式子,想出一个"+1-1"的方法。
我们在(x-1+n)%n+1-1%n,(1%n==1)
整理一下就是(x-2+n)%n+1了。👍。
或者把x-1+n写成x-2+n+1这样更加自然点。
列的表示比较简单,这里就不再展开了。
总结
今天又是蒟蒻的一天。