蓝桥杯“打印十字图”:从观察规律到二维数组分层实现

发布时间:2026/10/13 7:39:40
蓝桥杯“打印十字图”:从观察规律到二维数组分层实现
第一次做这道题的时候我对着样例一笔一笔画了半天明明图形看着就是“回”字套“十”字代码却怎么写怎么翻车。后来我换了个思路先别急着找公式直接把图形拆成“中心十字 外面的框 四角的连接段”十几分钟就把代码写顺了。这篇文章就把这套思路完整展开包括题目怎么拆、代码怎么组织、有哪些容易踩的坑适合正准备刷蓝桥杯历届试题、或者被各种文字图形题卡住的朋友。1. 这题到底在考什么文字图形题不是死记硬背是观察力加模拟能力1.1 看到题目第一眼应该想什么很多人拿到“打印十字图”这种题第一反应是“这不就是个双层循环输出星号吗”然后就开始猜坐标公式猜了一上午还不对。实际上这类题考察的不是某个高深算法而是两件事你能不能从图形样例里看出重复结构以及你能不能把这个重复结构转化成程序里的循环。题目要求输出一个“十字图”它本质上是一个由*和.组成的二维字符矩阵。输入是一个正整数n输出的是边长随n变化的一张图。n 越大图形越复杂。这种题的共同特点就是数据规模很小不需要考虑时间优化真正难的是“别把图形画错”。我建议拿到题目后的第一件事不是写代码而是拿纸笔把n1, n2, n3的图形画出来哪怕只是画出缩略结构。画的过程里你会看到很多重要信息图形是否上下左右对称、边长的增长规律、中心十字是否始终保持不变、外层和中心之间靠什么连接。1.2 为什么说这是个“小规模模拟题”这类打印图形的题时间复杂度和输入规模是线性平方关系。以n最大取 30 为例边长也就是 125 左右总共大约一万多个字符哪怕用三重循环去暴力判点现代电脑也就是一瞬间的事。所以不要一上来就追求什么“最简代码”。先把正确性保证住再考虑代码短不短。就我的经验来看用二维字符数组先把图形填充完最后统一输出是这类题最稳的做法。因为你可以随时断点查看数组看看哪一行哪个位置填错了比在printf里逐字符判断要直观太多。2. 先画图再写码把十字图拆成中心十字、外框和四角连接段2.1 中心和边长的关系这道题里有个非常关键的规律不管n多大图形中心始终是那个 5×5 的小十字。你可以把整个图形想象成从这个小十字开始一圈一圈向外扩展。边长公式是边长 4 * n 5也就是说n每增加 1边长就增加 4。为什么要加 5因为最里面那个小十字本身就是 5×5。这个 5 就像一个“基底”外面每多一层上下左右各扩展 2 个字符所以总边长加 4。中心坐标也好算就是边长的中点c 边长 / 2 2 * n 2只要抓住这一点后面所有外框坐标都能以中心为基准计算图形自然保持对称。2.2 外框怎么定位从中心往外看每一层外框距离中心都是偶数距离。最靠近中心十字的那个框是第 1 层它到中心的距离是 2再往外一层距离就是 4以此类推。如果用d表示某一层到中心的距离那么这一层的外框由四个边组成上边行号是c - d列号从c - d 2到c d - 2下边行号是c d列号范围相同左边列号是c - d行号从c - d 2到c d - 2右边列号是c d行号范围相同这个“每边两端都缩进 2 格”的处理保证外框四角空出来不会被填成完整矩形。因为完整的矩形框会跟中心十字连成一片乱麻就不像“十字图”了。2.3 四角连接段是灵魂如果你只画中心十字和外框整个图看起来是散的中心十字是中心十字外框是外框二者没有连起来。这时候就需要在四个角各补一小段“阶梯形”连接线。以左上角为例设这一层外框的左上角坐标为(r1, c1)其中r1 c - d, c1 c - d。需要额外填这几个点(r1 1, c1 2) (r1 2, c1) (r1 2, c1 1)这三个点加上外框本身的点会形成一个类似“L”形的小转折正好把中心十字延伸到外框。四角都做同样的处理图形就是整体连通的。我第一次做的时候就是漏了这一步输出的图形中心是一个十字、外围是一个框中间断成两截怎么看怎么丑。后来补上四角连接段才真正对味。3. 分层填充实现二维数组画十字图的全过程3.1 为什么选二维数组而不是直接 printf有些人喜欢边循环边printf一行一行输出。但文字图形题有个麻烦很多字符要跨行判断如果你一边算一边输出脑子得同时记住前面几行都填了什么非常容易乱。用二维数组g[][]做缓冲区就清晰很多先把所有位置初始化为.再根据规律把该填*的位置挨个填进去最后统一输出。这个思路特别适合嵌套图形因为你可以“先画中心再画外圈”顺序天然就是一层层向外。3.2 完整代码参考下面这段代码是我按“中心十字 逐层补框 四角连接”的思路实现的注释写得比较细方便对照上面的分析。#include bits/stdc.h using namespace std; const int MAXN 200; char g[MAXN][MAXN]; int main() { int n; cin n; int m 4 * n 5; // 边长 int c 2 * n 2; // 中心坐标 // 1. 全部初始化为 . for (int i 0; i m; i) for (int j 0; j m; j) g[i][j] .; // 2. 画中心十字半径 2 for (int k -2; k 2; k) { g[c][c k] *; // 横臂 g[c k][c] *; // 竖臂 } // 3. 从第 1 层外框开始逐层向外画 for (int d 4; d 2 * n 2; d 2) { int r1 c - d, r2 c d; // 上边行、下边行 int col1 c - d, col2 c d; // 左边列、右边列 // 3.1 画四条边 for (int j col1 2; j col2 - 2; j) { g[r1][j] *; // 上边 g[r2][j] *; // 下边 } for (int i r1 2; i r2 - 2; i) { g[i][col1] *; // 左边 g[i][col2] *; // 右边 } // 3.2 补四角连接段 // 左上角 g[r1 1][col1 2] *; g[r1 2][col1 2] *; g[r1 2][col1] *; g[r1 2][col1 1] *; // 右上角 g[r1 1][col2 - 2] *; g[r1 2][col2 - 2] *; g[r1 2][col2] *; g[r1 2][col2 - 1] *; // 左下角 g[r2 - 1][col1 2] *; g[r2 - 2][col1 2] *; g[r2 - 2][col1] *; g[r2 - 2][col1 1] *; // 右下角 g[r2 - 1][col2 - 2] *; g[r2 - 2][col2 - 2] *; g[r2 - 2][col2] *; g[r2 - 2][col2 - 1] *; } // 4. 输出整个矩阵 for (int i 0; i m; i) { for (int j 0; j m; j) cout g[i][j]; cout \n; } return 0; }3.3 这段代码的几个关键点第一中心十字的半径固定是 2不管n怎么变都不改。你可以把这部分看成整张图的“种子”。第二最外层框的d最大值是2 * n 2。因为中心坐标c 2 * n 2当d 2 * n 2时c - d 0刚好到达图形最上边再配合对称性四边都会落在边界上。第三四角连接段里有些点和中心十字或内层外框是重合的。比如左上角第一个点(r1 2, col1 2)在d 4时这个坐标就是中心十字左上臂的端点。重复填同一位置不会带来错误g[][]本来就是覆盖赋值反而省了判断是否越界的麻烦。4. 进阶坐标判断法一个循环直接判星号4.1 坐标法到底在做什么写完分层填充之后你会发现这段代码虽然思路清晰但还是有点长。有没有更短的做法有就是对每个坐标直接做一个布尔判断判断条件写对之后一个双层循环就能输出整张图。坐标判断法的核心是把(i, j)转换成相对于中心的偏移量。x abs(i - c) y abs(j - c) t max(x, y)这个t表示当前点离中心最远在哪一层。比如t 2时点在中心十字所在的小方块里t 4时点在第 1 层外框附近t 6时点在更外面一层附近。但要注意不是所有t相同的位置都填*。外框边上要填外框四角要空出来中心十字的横竖臂要填其他位置留点。所以坐标法的难点不在“算距离”而在“综合各种条件”。4.2 一种直观的判定骨架我比较推荐的写法是把“是否为星号”抽象成一个判断函数或一段逻辑按优先级判断。如果该点在中心十字的横臂或竖臂上输出 * 否则如果该点在某层外框的四条边上输出 * 否则如果该点在四角连接段上输出 * 其他情况输出 .这个骨架和前面二维数组填充是一一对应的。你完全可以把第 3 节代码里的填充逻辑改写成一组if条件然后放到双层循环里逐点判断。这样代码会短一些但可读性会下降。我更建议做题时先用二维数组方法把题做出来确保正确然后再去琢磨坐标判断法。因为坐标判断法一旦写错一个边界很难通过肉眼排查而二维数组方法可以随时把中间结果打印出来看。5. 实测中那些很容易翻车的细节5.1 数组大小和初始化这类题最冤枉的失误就是把数组开小了。边长最大是4 * n 5如果题目说n最大是 30那么最大边长是 125数组至少开到 150 以上才安全。很多题目会给n 30有些人习惯性开100的数组结果直接越界。另外二维数组初始化一定要做。如果不初始化里面的内容是未定义的垃圾值输出时会看到一堆乱码。用memset(g, ., sizeof(g))或者双重循环赋初值都行别偷懒省略。5.2 输出格式换行与空格图形题对输出格式的要求一般很严格每行结尾不要有多余空格行与行之间用换行符分隔。最常见的错误是在printf(%c , g[i][j])里多加了一个空格整张图就错位了。我一般用cout g[i][j]连续输出一行结束再cout \n。这种写法没有行尾空格也不容易手滑多打字符。注意题目要求用什么字符表示图形就用什么字符不要把*和$混用。5.3 图形自查方法写完程序别急着提交先用小数据肉眼核一遍。你可以把n1的输出结果跟题目样例逐字符比对。更高效的办法是写一个简单的对称性检查程序跑完后把生成的矩阵存下来检查它是否关于中心点、中心行、中心列对称。如果某个字符破坏了对称性说明填充逻辑里有问题而且问题点一定在对称轴的边界附近。这个方法我强烈推荐很多隐蔽 bug 就是靠它查出来的。6. 这类题还能怎么练文字图形题的通用打怪路线6.1 同类图形题的常见变体“打印十字图”只是文字图形题里的一个经典代表。类似的还有打印菱形、打印沙漏、打印回形矩阵、打印螺旋矩阵等。它们的共同点是图形由若干行字符组成规律是重复的、可拆解的。练这种题时可以按这个顺序来先手动画出n1, n2, n3的图形把每一层坐标写下来。找出最小的重复单元。比如十字图的最小重复单元是中心十字和外框的连接方式。用二维数组把重复单元填进循环先保证正确再考虑化简。用对称性检查结果别只看开头几行。6.2 从图形题里带走什么别小看这种“幼稚”的字符题它对代码基本功的锻炼很实在二维数组的使用、边界坐标的计算、循环嵌套的正确性、格式化输出的习惯全都考到了。有些人刷题只爱做算法题觉得输出图形没意思结果真上了考场遇到这类题反而手忙脚乱。根据我的体会做文字图形题最划算的投资是“画图”。在草稿纸上多画几张图把规律看明白了写代码就是顺水推舟的事。最忌讳的是连样例都没看懂就打开编辑器开始猜代码。先画两分钟图能省后面二十分钟调试时间这笔账怎么算都划算。