从第一行正中间的 1 开始,按四条互斥规则(右上出界回绕、右上角特判正下、被挡改下移)逐个落子,O(N²) 模拟填满幻方。
OJ: roj
题目 ID: 3628
难度:普及-
标签:模拟构造python
创建: 2026-10-02 11:19
更新: 2026-10-06 15:31
题目描述
幻方是由数字 3,输出为三行:8 1 6、3 5 7、4 9 2。
思路
题面规则本身就是算法:从起点
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-02 11:20
# update_at: 2026-10-02 11:20
import sys
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
square = [[0] * n for _ in range(n)]
row, col = 0, n // 2 # 数字 1 的落点:第一行正中间
for value in range(1, n * n + 1):
square[row][col] = value
# 四条规则按题面顺序判定:分支互斥,书写顺序即优先级。
# 后两个分支隐含 row > 0 且 col < n-1,所以下标始终在棋盘内。
if row == 0 and col != n - 1:
row, col = n - 1, col + 1 # 上边非右上角 → 最后一行、右一列
elif col == n - 1 and row != 0:
row, col = row - 1, 0 # 右边非右上角 → 第一列、上一行
elif row == 0 and col == n - 1:
row += 1 # 右上角 → 正下方
elif square[row - 1][col + 1] == 0: # 右上方为空 → 填右上方
row, col = row - 1, col + 1
else: # 右上方已被占 → 填正下方
row += 1
print('\n'.join(' '.join(map(str, line)) for line in square))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 15:31
* update_at: 2026-10-06 15:31
*/
// 按 NOIp2015 幻方的四条规则逐格模拟:向右上走、出界回绕、右上角特判正下、被挡改下移。
#include <cstdio>
typedef long long ll;
const int MAXN = 40; // N <= 39 且为奇数
ll n; // 幻方边长
ll square[MAXN][MAXN]; // square[r][c] = 该格填的数,0 表示还没填
// 按题面规则从 (row,col) 求数字 K 的落点
void move_next(ll &row, ll &col) {
if (row == 0 && col != n - 1) { // 规则 1:第一行且非最后一列,回绕到最后一行、列右移
row = n - 1;
col = col + 1;
} else if (col == n - 1 && row != 0) { // 规则 2:最后一列且非第一行,回绕到第一列、行上移
row = row - 1;
col = 0;
} else if (row == 0 && col == n - 1) { // 规则 3:右上角,填正下方
row = row + 1;
} else if (square[row - 1][col + 1] == 0) { // 规则 4:右上方为空,填右上方
row = row - 1;
col = col + 1;
} else { // 规则 4:右上方已被占,改填正下方
row = row + 1;
}
}
int main() {
scanf("%lld", &n);
ll row = 0; // 数字 1 放在第一行正中间
ll col = n / 2;
for (ll k = 1; k <= n * n; ++k) {
square[row][col] = k;
move_next(row, col); // 求下一个数的落点;填到最后一个数时结果不再使用
}
for (ll r = 0; r < n; ++r) {
for (ll c = 0; c < n; ++c) {
printf("%lld%c", square[r][c], c == n - 1 ? '\n' : ' ');
}
}
return 0;
}