1. 问题分析与理解
这个矩阵生成问题看似简单,但包含了几个需要仔细理解的区域划分规则。我们先来看一个N=1时的3×3矩阵示例:
code复制1 2 1
4 1 5
1 3 1
通过这个示例可以直观看到:
- 中心对角线(从左上到右下)和反对角线(从右上到左下)交叉点值为1
- 上半部分(两条对角线之间的上方区域)为2
- 下半部分(两条对角线之间的下方区域)为3
- 左侧部分(两条对角线之外的左侧区域)为4
- 右侧部分(两条对角线之外的右侧区域)为5
关键理解点:矩阵被两条对角线(主对角线和副对角线)划分为五个区域,每个区域的边界条件需要精确描述。
2. 区域划分的数学表达
2.1 矩阵索引与区域判定
对于一个(2N+1)×(2N+1)的矩阵,行列索引都从1开始计数。设当前元素位置为(i,j),则各区域可以表示为:
-
对角线区域(值为1):
- 主对角线:i == j
- 副对角线:i + j == 2N + 2
- 中心点:同时满足上述两个条件
-
上半三角区域(值为2):
- j > i 且 j < 2N+2-i
-
下半三角区域(值为3):
- j < i 且 j > 2N+2-i
-
左半三角区域(值为4):
- j < i 且 j < 2N+2-i
-
右半三角区域(值为5):
- j > i 且 j > 2N+2-i
2.2 边界条件验证
以N=2(5×5矩阵)为例验证边界:
code复制(1,1) (1,2) (1,3) (1,4) (1,5)
(2,1) (2,2) (2,3) (2,4) (2,5)
(3,1) (3,2) (3,3) (3,4) (3,5)
(4,1) (4,2) (4,3) (4,4) (4,5)
(5,1) (5,2) (5,3) (5,4) (5,5)
- 点(2,3):i=2,j=3 → 2N+2=6 → 3>2且3<4 → 满足上半三角条件
- 点(4,3):i=4,j=3 → 3<4且3>2 → 满足下半三角条件
- 点(3,1):i=3,j=1 → 1<3且1<4 → 满足左半三角条件
- 点(3,5):i=3,j=5 → 5>3且5>4 → 满足右半三角条件
3. 算法实现详解
3.1 数据结构选择
使用二维数组存储矩阵是最直接的选择:
cpp复制int arr[2*n+2][2*n+2]; // 行列都从1开始索引,多分配一些空间
注意:虽然题目中N≤8,最大矩阵为17×17,但实际编程竞赛中建议使用动态分配或vector更安全。
3.2 核心填充逻辑
填充过程采用双重循环遍历每个元素:
cpp复制for(int i = 1; i<=2*n+1; ++i) {
for(int j = 1; j<=2*n+1; ++j) {
if(j > i && j < 2*n+2-i) // 上半三角
arr[i][j] = 2;
else if(j < i && j > 2*n+2-i) // 下半三角
arr[i][j] = 3;
else if(j < i && j < 2*n+2-i) // 左半三角
arr[i][j] = 4;
else if(j > i && j > 2*n+2-i) // 右半三角
arr[i][j] = 5;
else // 对角线
arr[i][j] = 1;
}
}
3.3 输出格式控制
输出要求严格:
- 行内元素用空格分隔
- 行末不能有多余空格
- 数据间不能有空行
实现技巧:
cpp复制for(int i = 1; i<=2*n+1; ++i) {
int j;
for(j = 1; j<2*n+1; ++j) { // 前2n个元素带空格
cout << arr[i][j] << " ";
}
cout << arr[i][j] << endl; // 最后一个元素不带空格
}
4. 常见问题与调试技巧
4.1 边界条件错误
常见错误包括:
- 索引从0开始还是1开始混乱
- 区域判断条件写反(如把>写成<)
- 矩阵大小计算错误(应该是2N+1而非2N)
调试方法:
- 打印小矩阵(N=1)的中间结果
- 检查对角线元素是否正确
- 验证每个区域的边界点
4.2 输出格式问题
常见错误:
- 行末多空格
- 最后多空行
- 数字对齐问题(虽然本题不要求)
解决方法:
- 使用上述输出技巧
- 在本地先测试样例输入输出
- 使用文件对比工具检查输出差异
4.3 性能优化
虽然本题N很小,但可以思考:
- 能否不存储整个矩阵直接输出?
- 判断条件能否简化?
直接输出版本:
cpp复制void print_direct(int n) {
int size = 2*n+1;
for(int i=1; i<=size; ++i) {
for(int j=1; j<=size; ++j) {
int val;
if(j>i && j<size+1-i) val=2;
else if(j<i && j>size+1-i) val=3;
else if(j<i && j<size+1-i) val=4;
else if(j>i && j>size+1-i) val=5;
else val=1;
cout << val;
if(j<size) cout << " ";
}
cout << endl;
}
}
5. 算法复杂度分析
-
时间复杂度:O(N²)
- 填充矩阵:O((2N+1)²) = O(4N²+4N+1)
- 输出矩阵:同样O(N²)
-
空间复杂度:
- 存储矩阵:O(N²)
- 直接输出版本可优化到O(1)
6. 扩展思考
6.1 其他区域划分方式
可以尝试:
- 将矩阵划分为九宫格
- 圆形区域划分
- 螺旋形填充
6.2 可视化输出
对于更大的N,可以考虑:
- 用不同颜色表示不同区域
- 生成图像文件而非文本输出
- 使用图形库实时显示
6.3 输入输出重定向
在实际编程竞赛中:
cpp复制freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
这样可以方便测试大量数据,而不用手动输入。
7. 完整代码实现
以下是带注释的完整实现:
cpp复制#include <iostream>
using namespace std;
void print_matrix(int n) {
const int size = 2*n + 1;
int matrix[size+1][size+1] = {0}; // 1-based indexing
// Fill the matrix
for(int i=1; i<=size; ++i) {
for(int j=1; j<=size; ++j) {
if(j>i && j<size+1-i) {
matrix[i][j] = 2; // Upper triangle
}
else if(j<i && j>size+1-i) {
matrix[i][j] = 3; // Lower triangle
}
else if(j<i && j<size+1-i) {
matrix[i][j] = 4; // Left triangle
}
else if(j>i && j>size+1-i) {
matrix[i][j] = 5; // Right triangle
}
else {
matrix[i][j] = 1; // Diagonals
}
}
}
// Output the matrix
for(int i=1; i<=size; ++i) {
for(int j=1; j<=size; ++j) {
cout << matrix[i][j];
if(j < size) cout << " ";
}
cout << endl;
}
}
int main() {
int n;
while(cin >> n) {
print_matrix(n);
}
return 0;
}
8. 测试用例设计
有效测试用例应包括:
- 最小边界:N=1
- 最大边界:N=8
- 中间值:N=3, N=5
- 特殊检查:验证所有区域边界
示例测试:
code复制1
2
3
8
预期输出应验证:
- 矩阵尺寸是否正确
- 各区域值是否正确
- 输出格式是否符合要求
9. 实际编程中的经验分享
-
索引处理:在算法竞赛中,使用1-based还是0-based索引要一致。本题从1开始更直观。
-
条件判断顺序:区域判断条件的顺序很重要,必须先检查最特殊的对角线情况。
-
空间优化:对于N很大的情况(虽然本题限制N≤8),可以考虑逐行生成直接输出,减少内存使用。
-
调试技巧:对于复杂的矩阵填充问题,可以:
- 先打印小矩阵验证
- 添加调试输出显示关键判断结果
- 使用assert验证不变量
-
代码风格:良好的变量命名(如用row/col代替i/j)和注释可以提高代码可读性。