分数 20
全屏浏览题目
切换布局
作者 C课程组
单位 浙江大学
所谓“螺旋方阵”,是指对任意给定的N,将1到N×N的数字从左上角第1个格子开始,按顺时针螺旋方向顺序填入N×N的方阵里。本题要求构造这样的螺旋方阵。
输入格式:
输入在一行中给出一个正整数N(<10)。
输出格式:
输出N×N的螺旋方阵。每行N个数字,每个数字占3位。
输入样例:
5
输出样例:
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
我的答案:
分析过程:
- 螺旋方阵的填充模式为:先向右,然后向下,接着向左,最后向上,循环这四个方向。
- 向右和向左时,行数保持不变;向下和向上时,列数保持不变。
- 每完成一个方向的填充后,要缩小填充的范围。
- 填充完成的条件是已填入的数目等于 N×N。
解题过程:
- 初始化一个 N×N 的数组 matrix。
- 初始化当前方向为“向右”,当前行和列为 (0, 0)。
- 初始化一个计数器 counter 从1开始,每填充一个数字后加1。
- 根据当前方向进行填充,完成后改变方向。
- 根据方向,更新行或列的值。
- 当计数器大于 N×N 时,结束填充。
- 打印结果。
接下来,我们用 C 和 C++ 语言实现上述算法:
好的,我明白你的疑问。螺旋方阵的填充过程确实有点特殊。当我们沿着一个方向填充时,该方向上可填充的格子数量不是每次都一样的。具体来说,每完成一个循环(四个方向:右、下、左、上),其中的两个方向上可填充的格子数量都会减少一个。
详细分析如下:
1. **向右**:开始时,我们从最左边向右填充,可填充的格子数量是 N。
2. **向下**:当我们完成向右的填充后,开始向下填充,此时可填充的格子数量是 N-1,因为上边的第一行已经被填充。
3. **向左**:接着,我们向左填充,此时可填充的格子数量也是 N-1,因为下边的最后一行也已经被填充。
4. **向上**:最后,当我们向上填充时,可填充的格子数量是 N-2,因为除了第一行外,第二行也已经被填充。
接下来,当我们开始新的循环(第二次的右、下、左、上方向填充),可填充的格子数量会进一步减少。
这是因为在向右或向左填充时,由于上次循环中向下和向上的填充,已经使得上下两行被填充,所以此次循环中向右或向左时的填充格子数量会减少两个。相应地,在向下或向上填充时,由于上次循环中向右和向左的填充,已经使得左右两列被填充,所以此次循环中向下或向上时的填充格子数量会减少两个。
在代码中,我们通过检查每个格子是否已经被填充(即值为0)来决定是否继续当前方向的填充。这样,即使我们的循环设定了 N 次迭代,由于已填充的格子检查,我们可以自然地结束当前方向的填充并转向下一个方向。
C语言:
#include <stdio.h>
int main() {
int N;
scanf("%d", &N);
int matrix[N][N];
int row = 0, col = 0, num = 1, i;
while (num <= N * N) {
while (col < N && matrix[row][col] == 0) {
matrix[row][col++] = num++;
}
col--; row++;
while (row < N && matrix[row][col] == 0) {
matrix[row++][col] = num++;
}
row--; col--;
while (col >= 0 && matrix[row][col] == 0) {
matrix[row][col--] = num++;
}
col++; row--;
while (row >= 0 && matrix[row][col] == 0) {
matrix[row--][col] = num++;
}
row++; col++;
}
for (i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
printf("%3d", matrix[i][j]);
}
printf("\n");
}
return 0;
}
C++:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int N;
cin >> N;
vector<vector<int>> matrix(N, vector<int>(N, 0));
int row = 0, col = 0, num = 1;
while (num <= N * N) {
while (col < N && matrix[row][col] == 0) {
matrix[row][col++] = num++;
}
col--; row++;
while (row < N && matrix[row][col] == 0) {
matrix[row++][col] = num++;
}
row--; col--;
while (col >= 0 && matrix[row][col] == 0) {
matrix[row][col--] = num++;
}
col++; row--;
while (row >= 0 && matrix[row][col] == 0) {
matrix[row--][col] = num++;
}
row++; col++;
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
cout.width(3);
cout << matrix[i][j];
}
cout << endl;
}
return 0;
}
注意:两种实现都采用了简单的嵌套循环,其中内层循环负责向某一方向填充,外层循环则负责切换方向。
C语言循环体详细解释:
首先,外部的 `while (num <= N * N)` 代表当我们填入的数字没有超过 `N*N` 时,我们就继续填充矩阵。
接下来是四个内部的 while 循环,分别代表向右、向下、向左、向上四个方向的填充。
1. **向右**:
```c
while (col < N && matrix[row][col] == 0) {
matrix[row][col++] = num++;
}
col--; row++;
```
- `col < N` 保证列数没有超出范围。
- `matrix[row][col] == 0` 检查当前格子是否为空(即值为0)。
- `matrix[row][col++] = num++` 将数字填入当前格子,列数加1准备填充下一个格子,填入的数字加1。
- 最后 `col--` 和 `row++` 是调整到下一个方向的起始点。
2. **向下**:
```c
while (row < N && matrix[row][col] == 0) {
matrix[row++][col] = num++;
}
row--; col--;
```
- `row < N` 保证行数没有超出范围。
- `matrix[row][col] == 0` 检查当前格子是否为空。
- `matrix[row++][col] = num++` 将数字填入当前格子,行数加1准备填充下一个格子,填入的数字加1。
- 最后 `row--` 和 `col--` 是调整到下一个方向的起始点。
3. **向左**:
```c
while (col >= 0 && matrix[row][col] == 0) {
matrix[row][col--] = num++;
}
col++; row--;
```
- `col >= 0` 保证列数没有变为负数。
- `matrix[row][col] == 0` 检查当前格子是否为空。
- `matrix[row][col--] = num++` 将数字填入当前格子,列数减1准备填充下一个格子,填入的数字加1。
- 最后 `col++` 和 `row--` 是调整到下一个方向的起始点。
4. **向上**:
```c
while (row >= 0 && matrix[row][col] == 0) {
matrix[row--][col] = num++;
}
row++; col++;
```
- `row >= 0` 保证行数没有变为负数。
- `matrix[row][col] == 0` 检查当前格子是否为空。
- `matrix[row--][col] = num++` 将数字填入当前格子,行数减1准备填充下一个格子,填入的数字加1。
- 最后 `row++` 和 `col++` 是调整到下一个循环开始的起始点。
这四个内部循环结束后,如果整个矩阵还没填满,那么整个外部循环会再次执行,继续从当前位置开始螺旋填充。
总结:
从这道关于螺旋方阵的题目中,我们可以学到很多有用的知识和技能:
1. **问题分析**:在开始编码之前,我们需要首先分析和理解题目。对于一些问题,如果没有明确的解题思路,直接编写代码可能会陷入混乱。通过深入地分析题目,我们可以识别出问题的核心要点,并形成一个清晰的解题策略。
2. **数组操作**:这题涉及到了二维数组的操作。对于新手来说,熟练掌握二维数组的初始化、访问和修改是非常基础的。
3. **循环控制**:题目中多层循环的使用,尤其是内部的四个方向的循环,帮助我们理解如何在循环中适时改变方向、跟踪状态并作出决策。
4. **边界条件处理**:处理数组时经常需要处理边界情况,例如确保索引没有超出数组的边界。这道题很好地体现了这一点。
5. **模拟问题解决**:这道题要求我们模拟螺旋填充的过程,这种模拟类的问题在实际编程和算法竞赛中都非常常见。通过解决此类问题,我们可以加强对复杂过程模拟的能力。
6. **代码整洁和结构**:良好的代码应当具备清晰的结构和易读性。这道题给了我们一个机会,让我们思考如何组织代码,使其不仅能够工作,而且具有良好的可读性和可维护性。
7. **调试与测试**:完成代码编写后,我们需要进行测试。设计一些测试用例,特别是一些边界情况,可以帮助我们发现并修复代码中的错误。
总的来说,这道题目为我们提供了一个综合练习的机会,让我们运用和巩固在数据结构、算法和编程中学到的知识和技能。