这题到底在考什么
把填数过程看成一圈一圈向内收缩:顶行向右、右列向下、底行向左、左列向上。
对象
保留四条边界 top、bottom、left、right,以及下一个要填的 num;每填完一条边就把对应边界向内缩。
为什么能这样做
每个格子只被填一次,边界收缩能避免反复判断是否撞墙。
先避开的坑
最容易在奇数 n 的中心格多填或漏填,所以每走完一段要确认 num 还没超过 n*n。
n=4 的四边界收缩
演示进度
课堂要求:每一步都让学生说出当前保留了什么信息,以及下一步为什么不会漏答案。
把关键过程写成表
| 阶段 | 边界变化 | 填入位置 |
|---|---|---|
| 顶行 | top++ | (1,1) 到 (1,4) |
| 右列 | right-- | (2,4) 到 (4,4) |
| 底行 | bottom-- | (4,3) 到 (4,1) |
| 左列 | left++ | (3,1) 到 (2,1) |
当堂检查
为什么输出要用 setw(3)?
四边界法中 top++ 表示什么?
代码读慢一点
#include <bits/stdc++.h>
using namespace std;
int a[15][15];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
int num = 1, target = n * n;
int top = 1, bottom = n, left = 1, right = n;
while (num <= target) {
for (int i = left; i <= right; ++i) a[top][i] = num++;
top++;
for (int i = top; i <= bottom; ++i) a[i][right] = num++;
right--;
if (num <= target) {
for (int i = right; i >= left; --i) a[bottom][i] = num++;
bottom--;
}
if (num <= target) {
for (int i = bottom; i >= top; --i) a[i][left] = num++;
left++;
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
cout << setw(3) << a[i][j];
}
cout << "\n";
}
return 0;
}
读代码抓这几处
- 四个边界表示当前还没填的矩形范围。
- num <= target 防止中心位置后继续乱填。
- setw(3) 是本题输出格式关键。
复杂度与常见错误
复杂度:时间 O(n^2),空间 O(n^2)。
- 忘记判断 num <= target,奇数边长中心出错。
- 填完边界后没有收缩。
- 输出没有按 3 位宽右对齐。
源文件:Problems/P5731.md · Problems/P5731_solution.md