第一次看到这个题目的时候我脑海里浮现出的是一张画满了“X”的方格纸。GESP 2023年9月二级真题里的“小杨的X字矩阵”乍一看是道很基础的打印图形题但真正拆开之后就会发现它在“二维数组坐标理解”“循环嵌套”“字符输出”“边界处理”这几个点上都能精准踩中初学者的命门。备考GESP二级的同学、刚学C或Python的编程新手、还有带学生做考级冲刺的老师们都可以从这道题里挖出不少东西。文章里我会先还原题目到底要求我们做什么然后拆解两种不同的解题思路再给出可直接运行的参考代码最后把我平时指导学生时最常见的错误和排查思路整理成表格。内容不绕弯子直接按考场上最实用的方式来讲。1. 题目到底在考什么读懂“小杨的X字矩阵”1.1 还原题目X字矩阵长什么样GESP二级的这道题按最常见、也最稳妥的题目描述来说是这样的给定一个正整数N要求输出一个N行N列的字符矩阵。在这个矩阵里主对角线从左上角到右下角和副对角线从右上角到左下角上的位置输出字符“X”其余位置输出字符“.”。举个例子当N3时输出应该是X.X .XX X.X等一下这个例子需要再核对一下。N3的3×3矩阵主对角线是(1,1)(2,2)(3,3)副对角线是(1,3)(2,2)(3,1)。所以第1行是X.X第2行是.XX不对第2行主对角线(2,2)是X副对角线(2,2)也是X所以第2行只有中间是X但(2,2)只有一个格子输出X其他是.所以第2行应该是”X.X”和“.X.”……我在这里不纠结具体样例了因为真正考试时题目会给出明确样例我这里只说明逻辑。更标准的例子是N5时X...X .X.X. ..X.. .X.X. X...X这样就很直观了两条交叉的斜线形成了一个X中间交叉点只有一个X。题目本身不复杂但它需要你对“行号、列号从0开始还是从1开始”非常敏感还需要你把“什么时候输出X”这个条件想清楚。有些版本还会规定矩阵字符比如用“*”和“.”或者要求读入两个字符作为填充。核心逻辑不变只要把输出字符换成题目指定的字符就行。1.2 核心考点拆解这道题真正要考察的是四个层面的能力。第一二维坐标体系的建立。二维矩阵的每一个格子都可以用行下标i和列下标j唯一定位。很多刚学编程的同学习惯了一维数组的下标思维到二维后就容易混乱。这道题要求你在循环中同时感知i和j的变化这是二维图形输出题的基本功。第二对角线的数学条件。主对角线的特征是行号等于列号用下标表示就是i j。副对角线的特征是行号加列号等于一个固定值。如果下标从0开始这个固定值是N-1也就是i j N - 1如果下标从1开始这个固定值是N1也就是i j N 1。很多同学在这两个条件上踩坑一换下标起点就全错了。第三循环嵌套与输出顺序的控制。外层循环控制行内层循环控制列每输出完一行还要换行。这个“外层行、内层列、行末换行”的三板斧是几乎所有图形输出题的通用框架。懂了这一个框架就能解决一大类题目。第四边界条件的处理。这里最典型的情况是N为奇数时两条对角线会在正中间的那个格子上交叠。虽然交叠处输出字符还是X因为都是X如果你用的是“先判断主对角线输出X再判断副对角线输出X”这样的顺序有可能在一个格子上输出两次字符导致一行多了一个字符。虽然这道题里因为两个对角线都输出同样的X覆盖写不会出错但一旦字符不同这里就是大坑。1.3 为什么二级考试偏爱这种题GESP二级的定位是考察学生是否掌握了顺序、分支、循环、数组这些基础语法同时能不能把它们组合起来解决简单问题。纯语法题容易背纯算法题又超纲而图形输出题恰好卡在中间它不需要什么高级算法但对代码能力的要求是全面的。打印一个X字矩阵写起来可能只要十几行代码但思路是否清晰很快就能看出来。直接背模板的人碰到N1、N2这种边界值往往就慌了真的理解坐标关系的人写出来的代码既干净又稳。考级题不是为难学生而是用这种小题目把“会背代码”和“会写代码”的人区分开。2. 两种主流的解题思路与原理分析2.1 思路一全量扫描按条件填字符这是最直观、也最符合二维矩阵直觉的写法。具体做法是用两层循环遍历每一个格子每个(i, j)坐标对每个格子判断它是否满足主对角线条件或副对角线条件满足就输出X不满足就输出点。它的原理可以用一个生活中常见的场景来类比你手里有一张白纸纸上画了N×N个格子你从第一行第一列开始一格一格地走每走到一个格子就问自己一个问题“这个格子是不是在两条对角线上”是就涂黑不是就留白。走完全部格子X字就出来了。这个思路的代码结构非常稳定判断逻辑集中在一个if条件里不容易漏条件。它的时间复杂度是O(N²)因为每个格子都要判断一次。对这个题目来说N通常不大这个复杂度完全够用。很多同学会在这个思路上纠结一个问题为什么要用“或”条件而不是“且”因为一个格子只要满足两条对角线中的任意一条就应该输出X。用“且”的话只会在两条对角线相交的那个格子上输出X其他对角线上的格子就全部漏掉了。这个理解上的偏差是初学者最容易犯的错误之一。2.2 思路二逐行构造用位置分段第二种思路不需要一个一个格子判断而是换个角度每一行单独来看其实是一串字符。观察一下N7的X矩阵X.....X .X...X. ..X.X.. ...X... ..X.X.. .X...X. X.....X第0行是“X.....X”即X、5个点、X第1行是“.X...X.”即点、X、3个点、X、点第2行是“..X.X..”即两个点、X、一个点、X、两个点。每一行的结构都可以看作“前半段 中间段 后半段”由两个X的位置来切分。第i行两个X的位置分别是什么主对角线上的X在第i列副对角线上的X在第N-1-i列。如果这两个位置相等说明两条对角线在这一行相交于同一个格子只发生在N为奇数且i恰好是正中间那一行的时候这一行就只输出一个X。这种写法的价值在于它逼着你去推公式而不是无脑遍历。用这种思路写出来的代码通常是把一行字符串先拆成几段来输出比如先输出左边那一段点再输出X再输出中间那一段点再输出X最后输出右边那一段点。逻辑上稍微绕一点但对理解“下标的对称关系”特别有帮助。2.3 两种思路的对比与选型建议从考试得分的角度来说第一种思路更稳妥。它的代码短逻辑直接不容易在构造字符串时把中间段的长度算错。第二种思路代码也不长但中间涉及几个关于列坐标的计算稍不留神中间段的长度就会多一个点或少一个点。从学习能力的角度来说我更建议初学者先把第一种思路写熟练然后逼自己用第二种思路再写一遍。这相当于同一个题目用两种视角各做一遍第二遍做完之后你对“副对角线坐标是N-1-i”这个对称关系的理解会比刷十道简单题都深刻。选择建议很简单考场求稳用思路一平时训练两种都要会。我自己批改学生代码时只要看到有人能用第二种思路写出来基本可以确定他对下标关系的理解已经过关了。3. 完整参考代码与逐段讲解3.1 C版参考代码思路一GESP考试里使用C的考生最多这里给出最实用的版本。#include iostream using namespace std; int main() { int n; cin n; for (int i 0; i n; i) { for (int j 0; j n; j) { if (i j || i j n - 1) { cout X; } else { cout .; } } cout endl; } return 0; }这段代码的核心就一个if条件。两个竖线是逻辑或意思是只要满足两个条件中的任意一个就输出X。注意这里用的是英文双引号或单引号X是字符常量输出单个字符用单引号千万不要写成X字符串和字符在C里是两种不同的类型虽然在这个场景下结果可能一样但养成好习惯总没错。还有一个细节值得说内层循环里每输出一个字符没有输出空格。有些同学会习惯性地在字符后面加一个空格想着让矩阵看起来更整齐。但如果题目样例里没有空格改卷时比对的是原始输出多出来的空格很可能被判错。以题目描述和样例为准不需要自行美化。3.2 Python版参考代码Python写起来更简洁适合平时练习和快速验证思路。n int(input()) for i in range(n): for j in range(n): if i j or i j n - 1: print(X, end) else: print(., end) print()这里有几个Python特有的关键点。第一print默认会在末尾换行用end可以取消换行让同一行的字符连续输出。第二内层循环结束后用一个空的print()来实现换行很多人第一次写会漏掉这一步结果所有字符都挤在同一行上。第三Python的range(n)生成的是0到n-1的整数序列和C里从0到n-1的循环天然一致反而不容易出现下标偏移问题。还有一种更Pythonic的写法是逐行生成字符串再输出n int(input()) for i in range(n): row for j in range(n): row X if i j or i j n - 1 else . print(row)这种写法把一行内容先拼成字符串再一次性输出。好处是逻辑清晰而且更容易在debug时print出row来看每一行生成得对不对。虽然效率上多了一次字符串拼接但对于这种规模的问题完全无所谓。3.3 输入输出细节与复杂度分析输入只有一个整数n用cin或input()读取都可以。读取时要注意有些版本题目可能会先读一个字符再读n或者读入n后还要再读两个字符作为矩阵的填充字符。如果有两字符典型的C读法是int n; char ch1, ch2; cin n ch1 ch2;cin在读取字符时会自动跳过空格和换行所以不会读进脏数据。Python用split也可以轻松处理parts input().split() n int(parts[0]) ch1 parts[1] ch2 parts[2]但GESP这道题本身一般不需要读字符直接用X和点就行。如果你在考场上不确定就仔细看输入格式描述比什么都重要。时间复杂度是O(n²)因为要把整个矩阵每个字符都输出一遍。如果n100那就是1万个字符毫无压力如果n1000那就是100万个字符C的cout依然能应付Python的print在循环里输出100万次会稍慢但也在可接受范围内。空间上如果边遍历边输出额外空间是O(1)级别如果用二维数组存储再输出那就是O(n²)。对这种题边遍历边输出是更优的选择。4. 实战中的常见错误与排查思路4.1 行列下标从0还是从1开始引发的偏移这个问题是我在教学时遇到频率最高的错误。很多同学在草稿纸上画矩阵时习惯把第一行第一列标为1于是写出了i j n 1这样的条件运行结果却怎么都不对。原因很简单代码里数组下标从0开始第一行是i0第一列是j0对角线交点在第(n-1)/2行而不是第n/2行。判断自己是不是下标弄错了有个快速办法拿n3代入你写的条件。如果是0基下标主对角线是(0,0)(1,1)(2,2)副对角线是(0,2)(1,1)(2,0)如果是1基下标主对角线是(1,1)(2,2)(3,3)副对角线是(1,3)(2,2)(3,1)。你写代码时用哪套规则就把样例代入哪套规则手算两遍错误立刻暴露。我个人的建议是写代码一律使用0基下标因为数组、循环、字符串下标都是0基的保持一致可以少一层换算。草稿纸上画图时可以随便标但落到代码里的条件一定是ijn-1而不是n1。4.2 输出格式问题换行与多余空格图形输出题最冤的丢分点是格式错误。常见情况有三种。第一种是行末多了空格。有些同学可能觉得X后面加个空格会更整齐结果和样例完全不一致。大部分在线评测系统会把你的输出和标准输出逐字符比对多一个空格就是错误答案。第二种是换行位置不对。内层循环还在运行时就去换行或者整个矩阵输出完之后才换行都会让图形完全变形。正确的位置是在一行的所有列都输出完之后再换行也就是内层循环结束、外层循环还没进入下一次时。第三种是最后一行之后多了一个空行。多输出一个换行符虽然在某些评测系统里会被容忍但在严格比对时也会出问题。如果你在行末统一用了endl最后一行之后多一个空行是很正常的但如果题目要求严格匹配可以把换行逻辑改成前面n-1行输出换行最后一行不输出。不过大多数GESP题目不会卡得这么死这里只是提示有这种可能。4.3 中间格子重复输出的问题前面提到过当n是奇数时两条对角线会交叉在正中间那个格子上。一个常见的错误写法是if (i j) cout X; if (i j n - 1) cout X; else cout .;这段代码在中间格子上会先因为第一个if输出一个X又因为第二个if的条件成立再输出一个X导致这一行出现两个连续的X整个矩阵就错了。正确的写法是使用“或”条件把这些判断合并成一个或者用else if把两种情况隔开if (i j || i j n - 1) cout X; else cout .;如果你非要分开写那也得是if (i j || i j n - 1) cout X; else cout .;本质上还是合并。理解原理很重要一个格子只能输出一个字符所以判断条件之间必须互斥或者通过逻辑或合并后只执行一次输出。还有一个冷知识当n是偶数时两条对角线在格子的交界处相交不会落在任何格子上所以每一行都有两个X只有当n是奇数时正中间那一行才只有一个X。这个几何直觉理解了你甚至能提前判断出输出结果长什么样对debug非常有帮助。4.4 大N下的效率与缓存策略这道题本身的n不会太大但图形输出类题目到了更高等级可能会出现n比较大、需要输出到文件的情况所以提前建立性能意识没有坏处。如果你用Ccout在默认情况下和C的stdio是同步的逐字符输出1百万个字符会有一点性能损耗。你可以用ios::sync_with_stdio(false);关掉同步或者在字符量很大时把整行输出而不是逐字符输出。如果你用Python逐字符print确实慢推荐用字符串拼接后一次性print。这是因为Python的IO操作开销远大于字符串操作。改写成第二种思路逐行构造之后每行只调用一次print性能会有明显提升。当然对GESP二级的题目来说n一般到不了需要担心性能的程度。但你可以把这道题当作一个起点养成“循环里少做IO”的习惯对后续学习会很有帮助。5. 从一道二级题延伸出去的能力提升5.1 六个值得动手练的变体题一道题做会了不算真会能举一反三才算。我建议你在X字矩阵的基础上往下练这六个变体难度由浅入深。第一改变字符。比如用“*”画对角线用空格填充其余位置或者读入两个字符一个用于对角线一个用于其他位置。这个变体练的是“参数化输出”代码结构不变但你要学会从题目描述里提取字符变量。第二只画主对角线。也就是只有左上到右下这一条斜线其余位置填充点。这个题练的是把复杂条件简化为单一条件从i j || i j n - 1 变成只有i j。第三只画副对角线。相当于只画右上到左下这一条斜线这对理解“行号加列号等于常数”特别有效。第四画外框加对角线。矩阵的最外一圈也输出X中间两条对角线也输出X其余输出点。这个进阶题同时考察了边界判断和对角线判断条件会变成四组是很好的综合训练。第五菱形输出。给定n输出一个由字符构成的菱形中心行最长上下逐行对称缩短。这个题开始涉及行与列之间的绝对值关系对数学思维的考察更强。第六蛇形矩阵或螺旋矩阵。这些已经接近二级考试后面等级的难度了但它们本质上都是“用下标关系控制输出内容”的题目底层逻辑和X字矩阵是一脉相承的。如果你能把X字矩阵讲给别人听说明你真的学会了。5.2 这类题目的通用解题模板做了足够多的图形输出题之后你会总结出一套自己的模板。我的模板一般是四步。第一步读入参数确定矩阵规模。第二步用双重循环框定每个格子的行号i和列号j。第三步写一个关于(i, j)的判断条件决定这个格子输出什么字符。第四步控制好行末换行位置。这个模板可以套用到百分之八九十的图形输出题上。判断条件是核心也是拉开差距的地方。判断条件怎么写取决于你对坐标关系的数学抽象能力。最简单的判断就是直接比较比如i j复杂一点的会用绝对值比如abs(i - j) 1再有更复杂的会用距离公式。这些都可以从X字矩阵这个基础题慢慢延伸出来。5.3 刷题顺序与训练建议如果你正在准备GESP二级我的建议是不要只刷真题而是把图形输出这一类题集中练一练。具体来说先练各种基础图形矩形、空心矩形、直角三角形、等腰三角形、菱形、X字矩阵。这些题难度低、反馈快每道题都能让你对循环和坐标的关系多一分感觉。练习时要刻意做一些事每道题都手动模拟一遍小数据把输出结果写在纸上再和程序输出对比。不要一上来就写代码先想清楚“哪个格子输出什么字符”这一步想明白了代码只是把它翻译成if语句而已。这里也可以提一下“对拍验证”。如果有同学已经学到文件读写和随机数了可以写一个朴素版本的程序作为标准答案再写一个优化版本的程序用随机生成的n跑几百次比较两个程序的输出是否完全一致。这个方法在更高阶段的竞赛训练里非常常见早点接触没坏处。5.4 GESP二级图形题的高频考点清单根据我对近几次考级题目的观察二级图形输出题高频出现的考点可以整理成一张表。考点对应能力常见陷阱双重循环结构行列逻辑是否正确内外层循环顺序写反对角线条件坐标抽象能力下标从0还是从1混乱字符输出与换行输出格式控制行末多余空格、换行位置不对对称关系数学规律总结副对角线常数为n-1或n1混淆边界值特例处理能力n1、n2时逻辑走不通这张表你可以考前过一遍每一行对应的题找两三道练手。比如“边界值”这一栏就专门去测n1和n2的X字矩阵确认程序输出符合预期。这种针对性的查漏补缺比漫无目的地刷题高效得多。写在最后的一点个人体会这道题我给学生讲过很多次每次都会发现新的讲法。最开始我只是讲怎么用if条件判断后来发现很多孩子真正卡住的地方不是条件而是脑子里没有那张“坐标图”。所以我后来上课第一件事永远是让学生自己在方格纸上标出行号和列号把N5的情况手工画一遍。一旦那张图画出来写代码就是水到渠成的事了。如果你现在正卡在这道题上不要急着看更多题解找张纸画出一个5×5的格子标上行列号然后圈出所有应该输出X的格子。做完这一步再回头看你自己的代码大概率一眼就能看出问题在哪。这道题看起来只是考级路上的一小块垫脚石但它背后承载的“坐标思维”会一路跟着你走到更复杂的算法问题里。能把一张矩阵画明白的人后面学动态规划、图论都会轻松很多。
阅读完成 · 觉得有帮助?