1. 题目拆解这道题到底在考什么1.1 输入输出与真实压迫感第一次在蓝桥杯历届试题里看到“打印十字图”我第一反应是这不就是无脑输出几个字符嘛循环加判断就能搞定。结果真正动手写的时候光是把坐标系理清楚就花了大半个小时提交还错了两三次。后来复盘才发现这类“文字图形”题根本不是考你会不会for循环而是考你能不能从一团看似混乱的#和.里抽取出规律。题目需求很朴素输入一个正整数n输出一个由#和.组成的十字图形。图形尺寸随n变化n越大图形越大但是整体形状保持“十字嵌套”的结构。换句话说这题没有复杂的算法没有高级数据结构连排序都用不上但每年都有不少人在它上面栽跟头。原因很简单规律找错了或者找对了但实现时边界处理崩了。这类题适合谁如果你是正在刷蓝桥杯真题的备赛选手那这道题几乎是必刷的入门级“规律题”如果你只是对控制台字符画感兴趣想训练自己从图形中抽象规律的能力这道题也值得花半小时做一遍。它不依赖任何库几行代码就能跑复现成本极低但带来的思维训练却相当扎实。1.2 为什么文字图形题容易翻车我总结了一下文字图形题翻车通常有四个原因。第一想直接按行输出。很多人拿到题就盯着样例图形试图找出第 0 行、第 1 行、第 2 行的规律然后拼出一堆判断条件。一旦n变大行与行之间的变化不再线性这种思路就会迅速失控。十字图这类嵌套图形尤其不适合按行硬推因为每一行其实是“整幅图的局部切片”脱离了整体坐标系很容易看花眼。第二忽略字符初始状态。图形由#和.组成输出时必须保证非图形区域是.。有些解法一上来就默认数组全是空格结果输出以后整体错位而且这种错误在视觉上非常隐蔽一眼看过去甚至可能发现不了。第三坐标与层数对应错乱。十字图的核心变量是层数k和中心点坐标k每增加一层上下左右各向外扩展两格。如果这个“两格”的关系没想清楚代码里就会出现各种1、-1的补偿修正最后改来改去反而越改越乱。第四没有利用对称性。十字图天然具备上下对称、左右对称、中心对称。聪明的做法是先借助对称性把四分之一画好或者干脆画完整幅图利用循环避免重复劳动。但如果不从整体结构入手代码会越写越长还特别容易在对称位置漏掉几个点。所以这道题表面上是“打印图形”实际上是在考察两件事一是你能不能从图形中抽象出坐标规律二是你能不能把规律稳定地翻译成代码。这两件事恰恰是很多竞赛题的基本功。2. 核心破题思路别按行硬找规律按层画框2.1 从中心十字入手我踩过坑之后换了一种思考方式不再盯着每一行 “这个位置到底是点还是井号”而是把整幅图拆成两个部分——中心十字和外围框层。先看中心十字。无论n取多少图形正中间都有一条贯穿上下的大竖线和一条横在中间的大横线。竖线从第 0 行一直延伸到最后一行的中点横线则从第 2 列延伸到倒数第 2 列的中点。如果你拿n 1的图形来看中心那一列整列都是#从顶部直接拉到最底部中心那一行从左边数第 2 个字符开始到右边倒数第 2 个字符结束整行都是#。这个十字就是整幅图的中轴线后面所有嵌套的框都套在它身上。为什么中心横线比竖线短因为十字图的外框在四个角上要留出 2×2 的空缺横臂被左右两边的空缺截断而竖臂则直接穿出到上下边界。这个不对称是题目图形本身的特征不是代码 bug。很多人在这一步开始怀疑自己其实只要了解了题目设计就不会再纠结。代码实现上中心十字可以用两个循环直接画// 先假设 g 是一个 char 二维数组size 4 * n 5mid size / 2 for (int i 0; i size; i) { g[i][mid] #; // 竖线整列贯穿 } for (int j 2; j size - 2; j) { g[mid][j] #; // 横线从第 2 列到倒数第 2 列 }画完中轴线之后图形已经有一个“骨架”了剩下的工作就只剩下在骨架外面套一圈一圈的方框。2.2 嵌套矩形与“外扩两格”的规律中心十字确定以后再观察外围。你会发现整个图形是由若干个矩形边框嵌套起来的最外层是一个大方框往里一层是一个中等方框再往里是一个更小的方框直到中心点。这些框不是随便画的每往里一层矩形的左上角和右下角都往中心方向收缩两格。反过来说从内往外看每扩一层四个方向各延伸两格。这个规律太关键了。我们用层号k来表示当前正在画的框是第几层k从n开始递减一直到0。当k n时画的是最外层的框当k 0时画的是中心那个点。每一层矩形框的左上角坐标都是(mid - 2 * k, mid - 2 * k)右下角坐标则是(mid 2 * k, mid 2 * k)。每一层框内部都是一个标准的矩形边框所以只要从左上角出发依次画上边、下边、左边、右边四条线就能闭合这个方框。画完外层画内层层层嵌套再叠加前面画好的中心十字整幅十字图的轮廓就出来了。这个方法完全不需要判断“当前坐标该不该是#”只需要机械地按层填点逻辑简单代码自然不容易出错。2.3 参数推导4n5、mid2n2、len4k很多教程直接把size 4 * n 5和mid size / 2甩给你却没说为什么。这里我补一下推导过程。中心横线的长度是4 * n 1左右各留两个点为.那么整行宽度就是4 * n 1 4 4 * n 5这就是size的来源。中心点坐标取在正中间即mid size / 2因为size恒为奇数所以mid就是正中间位置。代入展开mid (4n 5) / 2 2n 2。再看每一层的边长。最外层矩形左上角(mid - 2n, mid - 2n)右下角(mid 2n, mid 2n)横向边长就是从mid - 2n到mid 2n一共4n 1个字符。如果我们用len 4 * k表示相对偏移量那么从坐标(x, y)画到(x len, y len)就刚好是这一层框的对角线范围。这样设计后k n就是最外层k 0就是中心点循环边界非常统一。我自己第一次做的时候没有推导公式而是直接写了一个size 4 * n 1结果输出图形明显小了一圈边缘被截断了。所以这种推导不是形式主义它直接影响最终代码的正确性。3. 代码实现C与Python双版本3.1 C完整代码与注释下面给出我最终采用的 C 解法。这个解法用二维数组存图先画中轴线再从外层到内层逐层画矩形框。#include iostream using namespace std; const int MAXN 205; char g[MAXN][MAXN]; void print_cross(int n) { int size 4 * n 5; int mid size / 2; // 初始化全为 . for (int i 0; i size; i) { for (int j 0; j size; j) { g[i][j] .; } } // 先画中心十字骨架 for (int i 0; i size; i) { g[i][mid] #; } for (int j 2; j size - 2; j) { g[mid][j] #; } // 再从外层到内层画矩形边框 for (int k n; k 0; k--) { int x mid - 2 * k; int y mid - 2 * k; int len 4 * k; for (int i 0; i len; i) { g[x i][y] #; // 左边竖线 g[x i][y len] #; // 右边竖线 g[x][y i] #; // 上边横线 g[x len][y i] #; // 下边横线 } } // 输出整个图形 for (int i 0; i size; i) { for (int j 0; j size; j) { cout g[i][j]; } cout \\n; } } int main() { int n; cin n; print_cross(n); return 0; }有几个点需要特别说明。第一为什么MAXN开 205蓝桥杯这类题n通常不会太大最多几十4 * n 5一般不会超过 200开 205 完全够用。如果n可以到 100那size就是 405需要把数组上限调整到 500 左右。最好的习惯是先计算size再定义数组大小或者干脆开一个足够大的静态数组。第二画矩形框的时候四个方向使用了同一个循环变量i所以四条边同时画。你可能会担心四条边在所有i上都执行会不会把已经画好的点覆盖掉实际上覆盖的字符也是#不会改变结果所以没有任何问题。第三k从n倒着循环到0和从小到大循环最终效果是一样的。我习惯从外到内因为这样更容易想象当前框的位置。但如果你觉得从内到外更顺也完全可以代码逻辑不用改。第四输出不要带多余空格。用cout g[i][j]一个字符一个字符输出行末换行就足够了。3.2 Python实现与移植要点如果你平时用 Python 刷题下面的版本可以直接用。核心思路和 C 完全一致只是把循环语法换成 Python 风格。def print_cross(n: int) - None: size 4 * n 5 mid size // 2 g [[.] * size for _ in range(size)] # 中心十字骨架 for i in range(size): g[i][mid] # for j in range(2, size - 2): g[mid][j] # # 从外层到内层画矩形边框 for k in range(n, -1, -1): x mid - 2 * k y mid - 2 * k ln 4 * k for i in range(ln 1): g[x i][y] # g[x i][y ln] # g[x][y i] # g[x ln][y i] # # 输出 for row in g: print(.join(row)) if __name__ __main__: n int(input().strip()) print_cross(n)Python 版本里最容易踩坑的是二维列表的初始化。[[.] * size for _ in range(size)]可以生成一个独立的二维列表但如果你写[[.] * size] * size所有行会引用同一个子列表改一行等于改所有行图形会变成一团乱麻。这个坑在 Python 里非常常见务必注意。另一个小细节是input()读入时可能有首尾空格或换行记得strip()一下再int()。蓝桥杯和其他 OJ 通常不会在样例里塞多余空格但养成好习惯总没错。3.3 时间复杂度与数组规模这个解法的复杂度很好算。初始化二维数组需要遍历size * size个格子画中轴线和所有矩形框需要遍历的次数加起来也差不多是size * size量级所以整体时间复杂度是O(size^2)也就是O(n^2)。对于n 30size 125循环次数只有一万多完全可以在瞬间跑完就算n 100size 405依然非常轻松。真正需要注意的不是时间而是数组大小。如果开的g不够大越界写入会破坏其他内存轻则输出错乱重则运行时错误。我在本地测试时曾经把MAXN设置成100然后输入n 30刚好size 125越界结果输出图形后面多了一串乱码。从那以后我每次都会先在心里算一遍最大可能值再决定数组尺寸绝不在这种小事上翻车。4. 调试实录我踩过的坑和定位技巧4.1 高频问题速查表刷题过程中我总结了这张高频问题表你可以直接对照排查。现象可能原因解决办法输出全部是.只有中间一行有#矩形框循环没有执行检查k的循环范围确认k是否真的从n开始中心竖线没有贯通到底竖线绘制循环范围写错竖线应该从i 0到size - 1不是只到mid外圈方框明显小一圈size公式写错确认size 4 * n 5不要写成4 * n 1图形左上角缺角矩形边循环少画了端点循环变量i要从0到len用不是Python 输出所有行都一样二维列表错误共享子列表用列表推导式初始化不要用* size行尾多了很多空格输出时把空格也打出来了只输出g[i][j]字符本身#和.之外不输出任何内容图形完全被#填满初始化成了#或没初始化确认初始化循环里赋值的是.这张表里的最后一个问题是我第一次做文字图形题时的真实经历。当时我图省事直接在声明数组的时候写char g[MAXN][MAXN] {#};以为把所有的元素都初始化为#然后只要把该挖空的地方填成.就行。结果输出的时候满屏都是井号自己还特别迷惑。后来才反应过来{#}只初始化了第一个元素剩下的默认还是0不是.也不是#。这种基础问题虽然低级但在考场上因为紧张很容易犯。4.2 调试技巧先让图“变形”再看规律一个非常实用的调试技巧调试阶段先把#临时换成*或者把.临时换成空格。这样图形在终端里会变得非常立体轮廓一眼就能看清。比如你可以临时这样输出if (g[i][j] #) cout *; else cout ;这样打印出来的不是题目要求的图形但它的轮廓和你最终想要的结构完全一致。如果变形图的外框和十字清晰可见那就说明坐标逻辑是正确的如果四角缺一块或者中轴线没有对齐也非常容易发现。问题定位以后再把输出改回标准字符就行。另一个技巧是手工模拟n 1的完整图。n 1时size 9只有 9 行 9 列你可以在草稿纸或者记事本里把每个坐标都标出来。例如想验证矩形框循环是否正确就画一个起点(2, 2)终点(6, 6)的方框然后对照代码输出逐步排查。图形题不怕笨办法反而很多时候最笨的办法最快。4.3 自测样例与边界检查做这题的时候我用三组数据自测n 1最小规模观察 9×9 的完整图形。n 2验证两层嵌套效果看矩形之间是否均匀错开两个字符。n 0这一组不是题目要求的合法输入但用来测试代码的健壮性也很有意思。size 5中心横线长度只有 1画出来其实就是中心一个小十字加一个点代码不会崩溃。n 1的输出大致是这样的具体以代码运行结果为准....#.... ....#.... ..#####.. ..#.#.#.. ..#####.. ..#.#.#.. ..#####.. ....#.... ....#....这组输出里最显眼的就是中心竖线贯穿上下四条边框嵌套在中间。如果你运行得到的结果里竖线断断续续那说明画中轴线的代码顺序可能有问题检查一下是不是在画矩形框的时候把中轴线覆盖了。不过只要覆盖的也是#一般不会出问题。5. 同类图形题的通用套路坐标系模拟与对称性5.1 文字图形题的共同特征刷过几道蓝桥杯和类似比赛的文字图形题之后我发现它们有一个共同点题目给你的是一张二维图你最好也用二维结构来解题。无论是打印字符三角形、回形矩阵、蛇形填数还是像十字图这样看似复杂的嵌套图形本质都是在二维坐标系里做“点”的标记。你要做的只有三件事定义坐标范围、确定填充规则、按顺序输出。这类题还有一个共同特征图形大多具有对称性。对称性意味着你不需要精确计算所有坐标只需要算出一部分再通过镜像或者中心对称补全另一部分。十字图如果从四分之一视角看规律会清晰很多回形矩阵则往往可以通过层数递归处理。凡是具备中心对称、轴对称图形的题都可以先把对称性画在草稿纸上再决定代码结构。5.2 从二维数组出发的通用解法框架我整理了一个可以套用在很多文字图形题上的解题框架size通常由题目给定的规模参数推导推导不出来就先看样例数一下最宽行的字符数。创建一个足够大的二维字符数组g全部初始化为背景字符。把图形拆成几个几何元素直线、矩形框、三角形、对角线等逐个用循环填点。如果图形是嵌套结构就设计一个层变量k每一层用统一的公式计算左上角和右下角坐标。最后按行输出每行直接把字符连接成字符串打印。这个框架不局限于十字图。比如“打印菱形”其实就是先画中心十字骨架再用两条斜线确定菱形边界比如“回字形矩阵”就是每层递归画边框和十字图的外框部分几乎一模一样。掌握了坐标系模拟的思路你在后续遇到类似题目时会更从容。5.3 用“镜像法”提速与简化有些追求极致简洁的选手会只画四分之一图形然后利用对称性翻转到其他区域。这个做法的优点是可以让核心循环缩小到原来的四分之一代码看起来更精炼缺点是思路稍微绕一点调试时也更容易产生坐标混淆。我的建议是如果你对坐标对称变换足够熟练可以尝试如果比赛时间紧张还是先保证用最直接的方法把题做对。十字图本身就非常对称四分之一区域画好后通过左右翻转和上下翻转就能生成完整图形。但是翻转过程需要额外写两层循环维护成本也不低。相比之下直接对整个二维数组填充矩形框的代码量并没有大多少而且每一步都直观可读。所以我在最终版本里没有用镜像法而是选择了最容易理解的全图填充。把正确率放在第一位这在我自己的比赛策略里永远是优先项。6. 写在最后的个人经验这道题做完以后我最大的体会是文字图形题真正难的不是写代码而是从视觉图形里抽离出几何结构。你盯着屏幕看十分钟不如拿支笔在草稿纸上把n 1的坐标圈一圈。只要你画出了中心十字和外圈矩形剩下的事情就只是把坐标公式翻译成循环。另外不要怕输出结果“不好看”。我第一次用这个思路画出来时总觉得和网上某些教程贴的图形不太一样一度以为自己错了。后来把四角和边框仔细核对了一遍发现无论从字符还是形状上都符合题目要求。不同教程的代码可能因为先画中轴线还是先画边框有差异最终图形看起来略有区别但只要每个坐标点都正确OJ 就会判你 AC。最后分享一个小技巧提交前用n 1和n 2各跑一次把输出保存到文件里然后用文本编辑器打开把.替换成空格把#替换成█你就能看到非常漂亮的“实心十字图”。这种视觉化验证方式能让你在最后关头快速发现潜在的结构错误。希望这篇整理能帮你少走一些我走过的弯路比赛时稳稳拿下这道题。