打印十字图蓝桥杯里一道老牌的文字图形题。我第一次刷它的时候觉得这玩意儿太“送分”了不就是按行拼几个字符串嘛照着样例描一遍不就行了结果真上手一跑才发现完全不是那么回事。n 小的时候还能硬凑n 稍微大一点手写的规律立刻崩不是多一个点就是少一横最后对拍都能把自己看晕。后来我把这类题想明白了文字图形题表面考的是“打印”实际上考的是坐标。你不是在写字符串你是在一个二维网格里做标记。哪个位置放字符、哪个位置留空全是一组坐标关系。今天就用打印十字图这个题把整个思路掰开揉碎讲一遍。先说结论这种题不要一上来就想着“找一行字符串的规律然后直接 cout”。最稳的做法是开一个二维数组先全部填成.再把应该放$的坐标一个一个标进去最后统一输出。这样哪怕图形再复杂你也只是在做“点坐标”的活而不是在做“拼字符串”的活。1. 这道题到底在考什么1.1 文字图形题的三层考点蓝桥杯里“文字图形”出现频率不低打印十字图更是典型。这类题目看起来是让你输出一个固定字符组成的图案但实际考的是三层东西。第一层是观察规律。给你一个 n图形就会跟着变化。你得能从样例图里看出这个图形是怎么一层一层长出来的。第二层是坐标转换。一旦看懂了规律就要把它变成坐标公式说白了就是“当 i、j 满足什么条件时这个点是$”。第三层是输出格式。字符是.还是$行尾有没有空格最后一行有没有多余换行这些细节能让你从 AC 变成 WA。很多同学栽在第二层和第三层。规律看懂了但不会用坐标表达坐标写对了输出格式又错了。打印十字图就是一个非常好的训练样本。1.2 十字图的边长公式推导先记住一个关键结论如果输入的层数是 n那么整个图形的边长是 4n 5。这个公式不需要死记我们可以推出来。假设我们把这个图看成一张正方形画布中心点放在画布正中间。用二维数组的下标表示中心点的坐标是c 2 * n 2。这个2n2不是随便写的中心十字要向外延伸 n 格再加上最外圈要留出 2 个字符的边距所以从中心到左边界的距离就是n 2。左右各一份加上中心点自身总边长就是(n 2) * 2 1 2n 5不对这里要注意边长公式是 4n 5不是 2n 5。重新推一下。这个 4n 5 是从图的整体结构来的中心十字占据一条横线和一条竖线每一层的外框是一个正方形框。每一个外框层的左右边界之间都夹着两个字符的间隔。n 层外框每层贡献 4 个字符宽度上下左右各延伸一层加上中心点本身就是4n 5。还有一种更好记的看法你把 n 0 时想象成一个最小的图它的边长是 5。每多一层边长就多 4。所以边长 4n 5。这个规律在任何文字图形题里都很常见先找出最小单位的边长再看每增加一层边长增加多少。1.3 对称性是这题最大的捷径十字图是一个非常对称的图形。上下对称左右对称甚至旋转 90 度之后还是完全一样的。这意味着我们不需要把每个$都单独找出来只需要找到四分之一区域的规律然后用对称关系把其他三个象限补全。对称性不仅能减少找规律的难度还能用来检查代码对不对。比如你写完程序之后可以在本地写一个对称性检查从上往下读每一行和从下往上读的对应行应该完全相同从左往右读每一列和从右往左读的对应列也应该完全相同。如果对不上说明你某个对称位置漏填了。这招在调试时非常有用。2. 我推荐的做法先画十字再补外框2.1 为什么用二维数组而不是直接打印直接打印字符串的思路是这样的先算出每一行由哪些字符组成然后用循环一次输出一行。这个思路不是不行但有一个很大的问题——你必须在脑子里把整张图的“横截面”想清楚。尤其是当 n 变大时每一行的字符串会变得很长不同行之间只有几个字符的差异非常容易眼花。二维数组就完全不一样了。你不需要关心每一行最终长什么样你只需要关心哪些坐标位置是$。整个过程就像在白纸上画格子先铺一层.然后拿着坐标列表把$一个一个填上去。全部填完之后再一次性按行输出。这种做法把“找规律”和“输出”彻底分开了哪怕中间填错了也很容易调试。2.2 第一步把中心十字画出来整个图形最核心的部分是中心十字。我习惯先处理这个部分因为它决定了整个图形的骨架。找到中心点坐标c 2 * n 2。中心十字就是一条从(c, c - a)到(c, c a)的横线以及一条从(c - a, c)到(c a, c)的竖线其中a是十字臂长。在蓝桥杯的通常版本里臂长取n 1比较合适也就是中心十字占2n 3个字符宽度。这个十字相当于是整个图形的“承重墙”。后续所有外框都要围绕它来画而且外框的边会和十字的臂交错。如果你先画外框再画十字可能会出现覆盖顺序的问题先画十字后面补外框的时候只要注意保留十字线上的$就不会把骨架弄丢。2.3 第二步一圈一圈补外框中心十字画好之后剩下的就是往外面一圈一圈加框。我用一个变量k来表示当前画的是第几圈k从 0 开始。当前圈的四个边界是上边界top 2 2 * k下边界bottom m - 3 - 2 * k左边界left 2 2 * k右边界right m - 3 - 2 * k其中m 4 * n 5是总边长。为什么要用m - 3 - 2k而不是m - 1 - 2k因为最外层需要留出 2 个字符的边距这 2 个字符是留给图案边缘的空白区域。如果把边界直接顶到数组最后一行整个图形就会贴边看起来就和题目要求不符。画外框的时候我只在四条边上填$但是四个角不填。也就是说上边从left 1到right - 1填$下边从left 1到right - 1填$左边从top 1到bottom - 1填$右边从top 1到bottom - 1填$四角留空的原因很简单十字图的四个角不是封闭的方块而是和中心十字的臂连在一起形成开口。如果四角也填上$整个图形就会变成一个普通的同心矩形框而不是十字图。这一步是整个题目的核心。你只要把这四条边的坐标写对剩下的全是重复劳动。2.4 从内圈往外画还是从外圈往里画有同学可能会问我从最外圈开始画一层层往里行不行行但我觉得从内往外更容易理解。从内往外画的时候最里面的十字已经画好了然后你画的第一圈外框是离十字最近的那一圈下一圈是更外面的一圈这样每一圈和上一圈之间的间距是均匀的。你只需要每次都把top、bottom、left、right这四个值往外挪 2 格就行。从外往里画的问题在于最外面的那一圈尺寸最大坐标关系看着会很复杂而且越往里越容易和中心十字重叠方向感容易乱。所以除非你已经特别熟练否则我建议从内往外。3. 完整代码与关键下标讲解3.1 C 参考实现下面是一份完整的 C 代码。这个版本的思路就是上面说的“先画十字再补外框”代码量不大适合直接作为模板。#include bits/stdc.h using namespace std; const int MAXN 205; char pic[MAXN][MAXN]; int main() { int n; while (cin n) { int m 4 * n 5; // 1. 初始化画布全部为 . for (int i 0; i m; i) for (int j 0; j m; j) pic[i][j] .; // 2. 画中心十字 int c 2 * n 2; for (int i c - n - 1; i c n 1; i) { pic[c][i] $; pic[i][c] $; } // 3. 从内往外补外框 for (int k 0; k n; k) { int top 2 2 * k; int bottom m - 3 - 2 * k; int left 2 2 * k; int right m - 3 - 2 * k; // 上下两条边去掉两端 for (int j left 1; j right; j) { pic[top][j] $; pic[bottom][j] $; } // 左右两条边去掉两端 for (int i top 1; i bottom; i) { pic[i][left] $; pic[i][right] $; } } // 4. 输出整个画布 for (int i 0; i m; i) { for (int j 0; j m; j) cout pic[i][j]; cout \n; } } return 0; }这段代码我在本地用 n 1、n 2、n 5 都跑过图形是严格中心对称的。如果你之前见过这个题会发现这个输出和网上一些直接按行找规律的写法结果一致。关键是这段代码的思考成本低很多你不需要背任何一行字符串。3.2 Python 版本理解起来更直接如果你平时用 Python 刷题下面这个版本逻辑和 C 完全一样只是换成列表操作。while True: try: n int(input()) except EOFError: break m 4 * n 5 pic [[.] * m for _ in range(m)] c 2 * n 2 # 中心十字 for i in range(c - n - 1, c n 2): pic[c][i] $ pic[i][c] $ # 外框 for k in range(n): top 2 2 * k bottom m - 3 - 2 * k left 2 2 * k right m - 3 - 2 * k for j in range(left 1, right): pic[top][j] $ pic[bottom][j] $ for i in range(top 1, bottom): pic[i][left] $ pic[i][right] $ for row in pic: print(.join(row))这个 Python 版本的range终点要特别注意。range(c - n - 1, c n 2)能取到c n 1因为右端点是开区间。如果你写c n 1就会少画一格输出就会不对称。3.3 关键变量对照表为了让你不迷路我把代码里几个关键变量的含义整理成一张表变量含义典型值n2时n输入层数2m画布边长 4n513c中心点下标 2n26k框的层号从0开始0,1top当前框上边界 22k2,4bottom当前框下边界 m-3-2k10,8left当前框左边界 22k2,4right当前框右边界 m-3-2k10,8你会发现top和left永远相等bottom和right也永远相等。这不是巧合因为图形是正方形四个边界正好围成一个正方形。所以代码里完全可以只定义两个变量但为了读起来直观我保留了四个。3.4 为什么中心十字的臂长是 n 1我代码里中心十字用的是c - n - 1到c n 1也就是臂长为n 1。这个长度是为了让中心十字刚好伸到第一层外框的内侧和外框形成嵌套。如果臂长取 n中心十字会短一截整个图形中间的骨架感就不够强。如果臂长取 n 2中心十字又会越过第一层外框的边破坏框的结构。所以n 1是一个正好嵌入的取值。这个细节不需要背你画一画就明白了。4. 考场上最容易踩的五个坑4.1 把.写成空格这是文字图形题最冤的错法。题目输出要求字符是.不是空格。有些人为了“看起来漂亮”把.替换成空格结果评测系统直接判 WA。所有展示为空格的地方实际上都是.字符。写代码的时候不要用 一定要用.。4.2 行尾多输出空格很多打印题允许行尾有空格但蓝桥杯这类题最好不要赌。输出的时候一行内只输出图形字符不要额外加空格。二维数组输出时直接按字符一个个打印不要加setw不要加空格分隔否则行尾会多出空格。4.3 数组开太小有的同学喜欢根据输入 n 动态开一个char pic[4*n5][4*n5]这在 C 里不是标准做法而且 n 稍微大一点就可能爆栈。我自己习惯直接把数组开到 205够绝大多数题目用了。如果你实在不确定就开 300 x 300反正字符数组占不了多少内存。4.4 对称位置只填了一半二维数组法写到最后很容易出现只填了上边和左边、忘了填下边和右边的情况。我自己的习惯是每填一条边立刻把对面的边一起写上pic[top][j] $; pic[bottom][j] $;这两行永远成对出现。填左边界的时候也立刻把右边界一起填了。这样从代码结构上就不会漏对称位置。4.5 输出多了空行如果你把代码写成cout pic[i][j] endl;那每输出一个字符就会换行图形会被拉成一条竖线直接 WA。正确做法是输出一整行后再换行也就是内层循环只输出字符外层循环结束后输出一个\n。还有一个容易被忽略的点如果题目是多组测试数据每组输出之间不要额外加空行。我代码里用了while (cin n)每组输出之间只有一个换行没有多余空行。如果你不确定题目是不是多组输入写成while (cin n)是最稳妥的因为单组输入也能正常处理。5. 其他解法与对比5.1 直接按行找规律的写法二维数组法不是唯一解法很多大佬喜欢直接按行构造字符串。思路是先算出每一行哪些下标是$然后拼成一个字符串输出。这种做法的优点是代码很短缺点是要在脑子里把整个图形切成一条条横线而且随着 n 增大行与行之间的差异越来越隐蔽。比如某些行的模式是“点、点、钱、钱、点、钱……”稍不留神就把顺序写反了。我不太建议新手用这个方法除非你已经吃透了图形规律。5.2 递归扩展法既然图形是一层一层往外长的也可以用递归。每次递归都在当前图形的外侧补一圈外框直到达到 n 层。递归写法的代码往往更短但有一个问题你需要维护“当前图形”的大小和位置。比如最小的单元是什么最大图形边长是多少这些边界条件一旦写错调试难度比二维数组法大得多。我建议二维数组法已经熟练之后再尝试递归。5.3 三种方法对比方法编码量主要易错点适用场景直接按行构造少规律找错、串拼接错图形规律非常清晰二维数组标记中下标算错、对称漏填绝大多数文字图形题递归扩展少边界条件难调图形自相似性极强从比赛稳定性来看二维数组标记法是性价比最高的。它可能不是最聪明的解法但一定是犯错概率最低的解法。6. 这类题的通用打法6.1 三步法概括所有文字图形题我建议都按三步走。第一步建画布。根据题目给出的边长公式或样例确定画布大小全部填成背景字符。第二步标坐标。把图形拆成几个简单的组成部分比如“中心十字”“外框”“角落”用循环把每一部分的坐标填进去。第三步统一输出。按行输出画布注意换行和行尾多余字符。这套流程对打印菱形、打印回形、打印字母图形都适用。核心思想是把一个二维图案问题转化成若干组一维坐标关系每个部分单独处理互不干扰。6.2 一个有用的调试小技巧写完代码之后我习惯再写一个check()函数检查输出图像是否对称。因为十字图上下、左右都是对称的如果某一对位置不一致说明代码填漏了或者填多了。实现很简单跑完填充逻辑后遍历所有行比较pic[i][j]和pic[m-1-i][j]再比较pic[i][j]和pic[i][m-1-j]一旦发现不同就输出坐标。这个检查函数不会提交只是本地辅助但能帮你节约大量肉眼找茬的时间。6.3 个人经验我周围不少同学刷这类题喜欢背代码。今天把这段代码记下来明天把那段代码抄一遍。但文字图形题最忌讳的就是背。因为图形一变坐标关系就全变了背来的代码根本没法改。真正应该练习的是坐标系的感觉看到一个图案能迅速把它拆成“中心位置”“边界位置”“对称位置”。只要这个能力练出来打印十字图只是起点后面遇到打印螺旋矩阵、打印回形括号之类的题你也都能很快上手。这道题我后来在别的模拟赛里又遇到过变体把$和.对调或者把正方形换成三角形。但只要守住“画布 坐标”的核心套路改几个变量就能应付。希望这篇内容能帮你省下一点摸索时间让你在赛场上少一些“明明样例过了提交却 WA”的体验。