云计算百科
云计算领域专业知识百科平台

GESP2026年9月认证C++四级( 第三部分编程题(2、有序网络))精讲



这道题 第三部分编程题第2题《有序网格》,把我们前面学过的几个知识点串在了一起:

二维数组 + 函数 + 循环 + 冒泡式交换 + 行排序 + 列排序。

这道题最重要的地方,是要理解一句话:

先横着整理,再竖着整理。顺序不能反!

下面我们像玩一个“数字方阵整理游戏”一样来学习。


一、数字王国举行“大扫除”

同学们,想象一下:

有一个数字王国,里面有一块巨大的棋盘。

棋盘上每个格子里都住着一个数字:

┌────┬────┬────┐
│ 6 │ 5 │ 4 │
├────┼────┼────┤
│ 3 │ 2 │ 1 │
└────┴────┴────┘

国王觉得:

“这里的数字太乱了!我要让整个棋盘变得有序!”

于是国王制定了两个命令:

命令①:先整理每一行

每一行从左到右:

从小到大排列。

例如:

6 5 4

整理成:

4 5 6


命令②:所有行整理完以后,再整理每一列

每一列从上到下:

从小到大排列。

注意!

国王特别强调:

🚨 必须先整理行,再整理列!

题目要求的正是:

一个二维网格,先对每一行从左到右升序排序,再对每一列从上到下升序排序,最后输出结果。


二、先看一个最简单的例子

假设有:

3行2列

输入:

6 5
4 3
2 1

我们把它画出来:

┌───┬───┐
│ 6 │ 5 │
├───┼───┤
│ 4 │ 3 │
├───┼───┤
│ 2 │ 1 │
└───┴───┘


三、第一关:横向整理每一行

第一行:

6 5

从小到大:

5 6

第二行:

4 3

变成:

3 4

第三行:

2 1

变成:

1 2

现在棋盘变成:

┌───┬───┐
│ 5 │ 6 │
├───┼───┤
│ 3 │ 4 │
├───┼───┤
│ 1 │ 2 │
└───┴───┘


四、第二关:竖向整理每一列

现在开始整理列。

第一列:

5
3
1

从上到下排序:

1
3
5

第二列:

6
4
2

排序:

2
4
6

最终:

┌───┬───┐
│ 1 │ 2 │
├───┼───┤
│ 3 │ 4 │
├───┼───┤
│ 5 │ 6 │
└───┴───┘

所以输出:

1 2
3 4
5 6

这正是试卷中的样例1。


五、这里有一个特别重要的“陷阱”

孩子可能会想:

“既然最后要行和列都排好,那我先排列,再排行,不也一样吗?”

不可以!

题目明确规定:

第一步:行排序

第二步:列排序

最终答案

我们必须严格按照题目的操作顺序。

可以把它想成:

🧹 先扫地,再拖地。

你不能说:

“我先拖地,再把灰扫一遍。”

否则结果当然可能不同。


六、现在问题来了:怎么在 C++ 里“整理一行”?

假设:

6 5 4 2

我们可以玩一个小游戏:

只比较旁边的两个数字。


第一次比较

看:

6 5

发现:

6 > 5

顺序错了!

交换:

5 6 4 2


第二次比较

看:

6 4

发现:

6 > 4

交换:

5 4 6 2


第三次比较

看:

6 2

发现:

6 > 2

交换:

5 4 2 6


我们发现一个很有意思的事情:

最大的数字 6 被“推”到了最右边。

这就是我们熟悉的:

🫧 冒泡排序思想

最大的数字就像一个大泡泡一样:

5 4 2 6

最大的
“泡泡”

一路向右冒。


七、题目里的参考程序就是这么干的

题目给出的参考程序没有直接使用 sort(),而是自己写了相邻元素交换的方法。

核心代码:

for (int j = 1; j < m; j++)
if (a[n][j] > a[n][j + 1]) {
int tmp = a[n][j];
a[n][j] = a[n][j + 1];
a[n][j + 1] = tmp;
}

我们逐步解析。


八、a[n][j] 是什么?

我们有:

int a[N][N];

这是一个二维数组。

可以想成:

a[1][1] a[1][2] a[1][3] …

a[2][1] a[2][2] a[2][3] …

a[3][1] a[3][2] a[3][3] …

第一个数字:

a[i]

代表:

第几行。

第二个数字:

[j]

代表:

第几列。

所以:

a[n][j]

就是:

第 n 行、第 j 列。


九、a[n][j] 和 a[n][j+1]

这两个家伙是谁?

例如:

第2行:

3 8 5 9
↑ ↑
j j+1

那么:

a[2][2]

是:

8

而:

a[2][3]

是:

5

于是程序比较:

if (a[n][j] > a[n][j + 1])

也就是:

如果左边比右边大,就交换它们。


十、交换数字怎么写?

比如:

8 5

我们要交换。

C++不能直接写:

a = b;
b = a;

因为这样会把原来的数字弄丢。

所以我们需要一个“小仓库”:

int tmp;

例如:

左边:8
右边:5

先:

tmp = 8;

变成:

tmp = 8
左边 = 8
右边 = 5

然后:

左边 = 右边;

变成:

左边 = 5
右边 = 5

最后:

右边 = tmp;

得到:

左边 = 5
右边 = 8

成功!

所以代码:

int tmp = a[n][j];
a[n][j] = a[n][j + 1];
a[n][j + 1] = tmp;

就是:

拿一个临时盒子,帮两个数字交换位置。


十一、把“整理一行”写成一个函数

题目参考程序专门写了一个函数:

void sort_row(int n)

这个函数的任务非常简单:

只负责整理第 n 行。

这就是函数思想:

主程序

├── 整理第1行 → sort_row(1)
├── 整理第2行 → sort_row(2)
├── 整理第3行 → sort_row(3)
└── …

这样程序就非常清楚了。


十二、sort_row() 里面发生了什么?

参考代码:

void sort_row(int n) {
for (int i = 1; i <= m; i++)
for (int j = 1; j < m; j++)
if (a[n][j] > a[n][j + 1]) {
int tmp = a[n][j];
a[n][j] = a[n][j + 1];
a[n][j + 1] = tmp;
}

return;
}

这里有两层循环。


外层循环

for (int i = 1; i <= m; i++)

相当于:

我要进行若干轮整理。


内层循环

for (int j = 1; j < m; j++)

表示:

从左到右检查相邻两个数字。

比如:

6 5 4 2
↑ ↑

然后:

6 5 4 2
↑ ↑

然后:

6 5 4 2
↑ ↑

不断比较:

第1个和第2个
第2个和第3个
第3个和第4个


十三、为什么要有两层循环?

这就是冒泡排序。

例如:

6 5 4 2

第一轮:

6 5 → 交换
5 6 4 2

6 4 → 交换
5 4 6 2

6 2 → 交换
5 4 2 6

最大的 6 到最右边。

第二轮:

5 4 → 交换
4 5 2 6

5 2 → 交换
4 2 5 6

第二大的 5 到了正确位置。

第三轮:

4 2 → 交换
2 4 5 6

完成!


十四、然后我们要“整理所有行”

主程序:

for (int i = 1; i <= n; i++)
sort_row(i);

如果:

n = 3

就相当于:

sort_row(1);
sort_row(2);
sort_row(3);

也就是:

整理第1行

整理第2行

整理第3行

到这里:

所有行都已经从小到大。


十五、但是故事还没有结束!

现在轮到:

🏰 “列管理员”出场!

题目要求:

所有行排完以后,再把每一列从上到下排序。

所以程序又写了一个函数:

void sort_col(int m)

这个函数负责:

整理第 m 列。


十六、整理列和整理行其实是“镜像操作”

整理行:

→ → → →

整理列:



例如:

5 6
3 4
1 2

我们整理第一列:

5
3
1

相邻比较:

5
3

发现:

5 > 3

交换:

3
5
1

再看:

5
1

交换:

3
1
5

继续下一轮:

1
3
5

于是第一列整理完成。


十七、列排序的代码

参考程序:

void sort_col(int m) {
for (int i = 1; i <= n; i++)
for (int j = 1; j < n; j++)
if (a[j][m] > a[j + 1][m]) {
int tmp = a[j][m];
a[j][m] = a[j + 1][m];
a[j + 1][m] = tmp;
}

return;
}

注意这里:

a[j][m]

第一个下标:

j

不断变化。

所以:

行号在变化。

而:

m

固定。

所以:

列号固定。

这正是:

固定一列,从上往下比较。


十八、孩子特别容易把两个函数弄混

可以这样记:

整理行

a[n][j]

n固定
j变化

所以:

第n行
→ → → →


整理列

a[j][m]

j变化
m固定

所以:

第m列



十九、我们用一个稍微复杂的例子完整走一遍

题目样例2:

1 3 2 5
6 2 4 4
5 4 1 3

这是试卷中的输入样例2。


第一步:每一行排序

第一行:

1 3 2 5

变成:

1 2 3 5

第二行:

6 2 4 4

变成:

2 4 4 6

第三行:

5 4 1 3

变成:

1 3 4 5

所以现在:

1 2 3 5
2 4 4 6
1 3 4 5


二十、第二步:每一列排序

第一列:

1
2
1

排序:

1
1
2

第二列:

2
4
3

排序:

2
3
4

第三列:

3
4
4

已经有序。

第四列:

5
6
5

排序:

5
5
6

最后:

1 2 3 5
1 3 4 5
2 4 4 6

这正是试卷给出的样例2输出。


二十一、现在来看完整程序

题目参考程序如下:

#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 15;

int n, m;
int a[N][N];

// 排序第 n 行
void sort_row(int n) {
for (int i = 1; i <= m; i++)
for (int j = 1; j < m; j++)
if (a[n][j] > a[n][j + 1]) {
int tmp = a[n][j];
a[n][j] = a[n][j + 1];
a[n][j + 1] = tmp;
}
return;
}

// 排序第 m 列
void sort_col(int m) {
for (int i = 1; i <= n; i++)
for (int j = 1; j < n; j++)
if (a[j][m] > a[j + 1][m]) {
int tmp = a[j][m];
a[j][m] = a[j + 1][m];
a[j + 1][m] = tmp;
}
return;
}

int main() {
scanf("%d%d", &n, &m);

for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &a[i][j]);

// 先排序所有行
for (int i = 1; i <= n; i++)
sort_row(i);

// 再排序所有列
for (int i = 1; i <= m; i++)
sort_col(i);

// 输出
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
printf("%d%c", a[i][j], " \\n"[j == m]);

return 0;
}


二十二、把整个程序想象成“两个机器人”

我们有两个机器人:

🤖 横向机器人

sort_row()

只干一件事:

把一行整理好。


🤖 竖向机器人

sort_col()

只干一件事:

把一列整理好。


主程序就是机器人队长:

👑 队长

┌────────┴────────┐
↓ ↓
🤖横向机器人 🤖竖向机器人
sort_row sort_col
│ │
↓ ↓
所有行排序 所有列排序
│ │
└────────┬────────┘

输出

这其实就是非常好的模块化编程思想:

一个函数只负责一件事情。


二十三、要注意行与列:

程序中:

int n, m;

表示:

n = 行数
m = 列数

例如:

3行4列

就是:

n = 3
m = 4

二维数组:

a[i][j]

可以记成:

第 i 行,第 j 列。


二十四、为什么数组开成 a[15][15]?

参考程序:

const int N = 15;
int a[N][N];

题目中的所有数据都能覆盖。


二十五、最后输出部分有一个“小机关”

程序最后:

printf("%d%c", a[i][j], " \\n"[j == m]);

其实它只是在解决一个问题:

每行最后一个数字后面要换行,其他数字后面要空格。

例如:

1 2 3
4 5 6


当:

j == m

说明:

已经到了这一行最后一个数字。

所以:

j == m

为真。

程序选择:

\\n

换行。

如果还没到最后:

j != m

就选择:

' '

空格。


同学们完全可以把它理解成:

if (j == m)
printf("%d\\n", a[i][j]);
else
printf("%d ", a[i][j]);

这样反而更容易理解。


二十六、这道题最重要的“程序结构”

同学们最后一定要能够自己说出:

① 建立二维数组

② 输入整个网格

③ 一行一行排序

④ 一列一列排序

⑤ 输出最终网格

也就是:

⭐ “先横后竖”

这是这道题的灵魂。


二十七、这道题考查的知识点

知识点在题目中的作用
二维数组 保存整个数字网格
a[i][j] 定位第 i 行第 j 列
for循环 遍历行、列
if 判断两个数字是否需要交换
临时变量 tmp 完成两个数字交换
函数 分别完成行排序和列排序
冒泡排序思想 完成一行/一列的整理
函数调用 sort_row(i)、sort_col(i)
scanf/printf 输入和输出

二十八、给孩子一个“考场思维模板”

以后看到类似题目:

二维数组 + 每行排序 + 每列排序

马上在脑袋里出现:

二维数组

a[i][j]

┌───────┴───────┐
↓ ↓
排一行 排一列
↓ ↓
a[n][j] a[j][m]
↓ ↓
j变化 j变化
n固定 m固定
↓ ↓
←→←→←→ ↑↓↑↓↑↓

然后:

先 row
后 col


二十九、容易错的5个地方

❌ 1. 把行和列搞反

a[i][j]

一定记住:

第一个是行,第二个是列。


❌ 2. 行排序时乱改第一个下标

行排序:

a[n][j]

应该:

n固定
j变化


❌ 3. 列排序时乱改第二个下标

列排序:

a[j][m]

应该:

j变化
m固定


❌ 4. 忘记题目规定的顺序

一定:

行 → 列

不是:

列 → 行


❌ 5. 交换时没有临时变量

不要写:

a = b;
b = a;

应该:

int tmp = a;
a = b;
b = tmp;


三十、给大家一个超级好记的口诀

🧙‍♂️ 有序网格整理术:

第一招:横着排

一行一行排
左小右大

第二招:竖着排

一列一列排
上小下大

第三招:两个数打架怎么办?

左 > 右

交换!

第四招:谁负责什么?

sort_row → 整理行
sort_col → 整理列

第五招:最重要!

先横后竖,顺序不能乱!


附新方法:

除了官方提供的参考程序之外,我们也可以采用“ sort+矩阵转置 ”来解决这个问题。


一、先回到题目:我们到底要做什么?

题目的任务是:

给一个 n × m 的二维网格,先把每一行从小到大排序,再把每一列从上到下排序,最后输出。

比如:

6 5
4 3
2 1

第一步:排每一行。

5 6
3 4
1 2

第二步:排每一列。

1 2
3 4
5 6


二、第一个问题:行排序非常简单

我们已经学过:

sort()

比如:

int a[5] = {6, 5, 4, 3, 2};

sort(a, a + 5);

就可以变成:

2 3 4 5 6

所以二维数组中的一行,也可以直接用 sort()!

假设:

int a[15][15];

第 i 行是:

a[i][1]
a[i][2]
a[i][3]

a[i][m]

那么:

sort(a[i] + 1, a[i] + m + 1);

就可以把第 i 行排好。


三、为什么是 a[i] + 1?

这是小学生第一次看到时最容易懵的地方。

假设:

int a[15][15];

我们采用:

1号行
1号列

开始使用。

那么:

a[i][1]

就是这一行的第一个数字。

而:

sort(a[i] + 1, a[i] + m + 1);

表示:

从 a[i][1] 开始,一直到 a[i][m],全部进行排序。

因为 sort() 的右边界是不包含的。

所以:

sort(开始位置, 结束位置)

是:

[开始位置,结束位置)

因此:

sort(a[i] + 1, a[i] + m + 1);

正好排序:

a[i][1] ~ a[i][m]


四、所以第一步只需要几行代码

for (int i = 1; i <= n; i++) {
sort(a[i] + 1, a[i] + m + 1);
}

是不是比手写冒泡排序简单很多?

原来的思路是:

比较

交换

比较

交换

再比较

再交换

现在我们直接:

sort!

就完成了。


五、可是第二步遇到麻烦了

现在假设经过第一步:

1 2 3
4 6 5
7 9 8

我们需要排序每一列。

比如第一列:

1
4
7

第二列:

2
6
9

第三列:

3
5
8

问题来了:

sort() 最擅长的是“一行一行排序”。

但是现在我们需要:

一列一列排序。

怎么办?


六、我们会想到一个办法

我们问自己:

sort() 不会排列,那我能不能把“列”变成“行”?

当然可以!

这就需要一个神奇操作:

⭐ 矩阵转置


七、什么叫“矩阵转置”?

听起来很高深,其实特别简单。

比如原来的表格:

1 2 3
4 5 6
7 8 9

把:

第1行
第2行
第3行

变成:

第1列
第2列
第3列

也就是:

原来的:
1 2 3
4 5 6
7 8 9

转置以后:

1 4 7
2 5 8
3 6 9

你会发现:

原来的第1列:
1
4
7

变成:

新数组的第1行:
1 4 7

太巧妙了!


八、用“搬家”来理解转置

我们可以这样理解:

原来的数字住在:

🏠 原来的二维数组

现在我们准备一栋新房子:

🏠 新的二维数组 b

然后规定:

原来的“第 i 行、第 j 列”的数字,搬到新房子的“第 j 行、第 i 列”。

也就是:

b[j][i] = a[i][j];

这就是转置的核心代码。


九、举一个数字看看

原数组:

a:

1 2 3
4 5 6
7 8 9

比如:

a[2][3]

是多少?

6

按照转置规则:

b[3][2] = a[2][3];

所以:

b[3][2] = 6


再比如:

a[1][2] = 2

那么:

b[2][1] = a[1][2];

所以:

b[2][1] = 2


十、于是整个二维数组就“翻转”了

原来:

a:

1 2 3
4 5 6
7 8 9

执行:

b[j][i] = a[i][j];

以后:

b:

1 4 7
2 5 8
3 6 9

注意:

行和列的位置交换了。

所以叫:

转置

可以让孩子记一句:

转置就是:行列互换。


十一、这时候发生了一件特别神奇的事情

原来:

a:

1 2 3
4 6 5
7 9 8

我们真正想排序的是:

列:

1 2 3
4 6 5
7 9 8

↓ ↓ ↓

但是转置以后:

b:

1 4 7
2 6 9
3 5 8

你发现了吗?

原来的:

第一列:
1
4
7

现在变成:

第一行:
1 4 7

原来的:

第二列:
2
6
9

现在变成:

第二行:
2 6 9

原来的:

第三列:
3
5
8

现在变成:

第三行:
3 5 8


十二、哇!现在可以再次使用 sort() 了!

我们终于把:

排序列

变成了:

排序行

所以:

for (int i = 1; i <= m; i++) {
sort(b[i] + 1, b[i] + n + 1);
}

注意这里有一个非常重要的变化。

原来 a 是:

n 行 × m 列

转置以后 b 是:

m 行 × n 列

所以:

a:

n × m

变成:

b:

m × n


十三、举例说明

假设:

a:

1 2 3
4 6 5
7 9 8

转置:

b:

1 4 7
2 6 9
3 5 8

现在对 b 的每一行排序。

第一行:

1 4 7

不用动。

第二行:

2 6 9

不用动。

第三行:

3 5 8

也不用动。

如果原来的列是乱的,比如:

a:

1 2 3
6 4 5
7 9 8

转置:

b:

1 6 7
2 4 9
3 5 8

如果继续:

sort(b[i] + 1, b[i] + n + 1);

就可以把这些“原来的列”分别排好。


十四、排完以后怎么办?

现在 b 已经排好了。

但是题目最后要求输出的是:

原来的 n × m 网格

所以我们需要:

再转置一次!

也就是:

a[i][j] = b[j][i];

是不是和刚才反过来了?

刚才:

b[j][i] = a[i][j];

现在:

a[i][j] = b[j][i];


十五、为什么转置两次可以回来?

可以把它想成:

🪞 镜子照一次,左右反过来。

🪞 再照一次,又回来了。

比如:

第一次转置:

1 2 3
4 5 6

1 4
2 5
3 6

第二次:

1 4
2 5
3 6

1 2 3
4 5 6

所以:

⭐ 转置两次 = 回到原来的形状


十六、完整算法就出现了

现在整个题目可以浓缩成:

原始二维数组

① 每一行 sort

行已经有序

② 转置

原来的“列”变成现在的“行”

③ 每一行 sort

原来的“列”已经有序

④ 再转置

恢复原来的 n × m 形状

⑤ 输出

是不是非常漂亮?


十七、我们用样例完整走一次

题目样例2:

1 3 2 5
6 2 4 4
5 4 1 3

题目要求最终得到:

1 2 3 5
1 3 4 5
2 4 4 6

这个样例及其输出见题目原文。


第一步:每行 sort

原来:

1 3 2 5
6 2 4 4
5 4 1 3

每行排序:

1 2 3 5
2 4 4 6
1 3 4 5


十八、第二步:转置

现在:

a:

1 2 3 5
2 4 4 6
1 3 4 5

转置以后:

b:

1 2 1
2 4 3
3 4 4
5 6 5

注意看:

b第1行:
1 2 1

它其实就是:

a第1列:
1
2
1


十九、第三步:对 b 的每一行 sort

第一行:

1 2 1

变成:

1 1 2

第二行:

2 4 3

变成:

2 3 4

第三行:

3 4 4

不变。

第四行:

5 6 5

变成:

5 5 6

所以:

b:

1 1 2
2 3 4
3 4 4
5 5 6


二十、第四步:再次转置

把:

b[j][i]

放回:

a[i][j]

得到:

1 2 3 5
1 3 4 5
2 4 4 6

完成!


二十一、最终代码

按照这个思路,可以写成一个非常清晰的版本:

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 15;

int n, m;
int a[N][N];
int b[N][N];

int main() {
cin >> n >> m;

// 读入二维数组
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}

// 第一步:每一行从小到大排序
for (int i = 1; i <= n; i++) {
sort(a[i] + 1, a[i] + m + 1);
}

// 第二步:转置
// 原来的列,变成新的行
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
b[j][i] = a[i][j];
}
}

// 第三步:对转置后的每一行排序
// 其实就是在排序原来的每一列
for (int i = 1; i <= m; i++) {
sort(b[i] + 1, b[i] + n + 1);
}

// 第四步:再次转置,恢复到原来的形状
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
a[i][j] = b[j][i];
}
}

// 第五步:输出
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cout << a[i][j];

if (j == m)
cout << '\\n';
else
cout << ' ';
}
}

return 0;
}


二十二、这份代码和官方参考程序最大的区别

官方参考程序的思路是:

自己写排序

sort_row()

手写相邻比较、交换

sort_col()

再次手写相邻比较、交换

而我们现在的方法:

sort()

行排序

转置

sort()

列变成行

再次排序

转置回来


二十三、这个方法漂亮的地方在哪里?

就在这里:

我想排序列

sort不会直接排列

┌──────────────┐
│ 那就把列变成行 │
└──────────────┘

转置

原来的列 → 新的行

sort()

排序完成

再转置

回来了

这其实是一种非常重要的算法思想:

⭐ “把不会做的问题,转换成会做的问题。”


二十四、这比“记住列排序代码”更重要

以后大家遇到一个问题:

“我不会处理列。”

不要马上想:

“我要不要重新写一套代码?”

而应该问:

“能不能把列变成行?”

如果可以:



转置



sort()

问题就解决了。

这就是一种很有价值的问题转换思想。


二十五、讲一讲 i 和 j 的变化

这里尤其容易混淆。

原数组:

a[i][j]

转置:

b[j][i] = a[i][j];

也就是:

原来的:

第 i 行
第 j 列

新的:

第 j 行
第 i 列

所以:

i 和 j 交换位置了。

可以记:

🪄 转置魔法

a[i][j]

b[j][i]

一句口诀:

“行列交换,两个下标也交换!”


二十六、为什么 b 的大小也发生变化?

假设:

n = 3
m = 4

那么原数组:

a:

3行4列

转置后:

b:

4行3列

所以:

a:n × m

b:m × n

这也是为什么:

sort(a[i] + 1, a[i] + m + 1);

而第二次变成:

sort(b[i] + 1, b[i] + n + 1);

前者每行有 m 个数字。

后者每行有 n 个数字。


二十七、孩子最容易犯的几个错误

❌ 错误1:转置写成

b[i][j] = a[i][j];

这根本没有转置。

应该是:

b[j][i] = a[i][j];


❌ 错误2:第二次排序仍然使用 m

错误:

sort(b[i] + 1, b[i] + m + 1);

因为 b 已经变成:

m行n列

所以每行有 n 个元素。

正确:

sort(b[i] + 1, b[i] + n + 1);


❌ 错误3:忘记转回来

排序完 b 以后不能直接输出 b,因为:

b 是 m × n

而题目要求输出:

n × m

所以必须:

a[i][j] = b[j][i];


❌ 错误4:把顺序搞反

一定是:

① 排行
② 转置
③ 排行
④ 转置回来

不能一开始就转置。

因为题目要求:

先每行排序,再每列排序。


二十八、最后给同学们画一张“藏宝图”

整道题可以记成:

有序网格


┌───────────────┐
│ ① 行排序 │
│ sort() │
└───────┬───────┘

┌───────────────┐
│ ② 转置 │
│ a[i][j] │
│ ↓ │
│ b[j][i] │
└───────┬───────┘

原来的“列”
变成新的“行”

┌───────────────┐
│ ③ 行排序 │
│ sort() │
└───────┬───────┘

原来的列排好了

┌───────────────┐
│ ④ 再转置 │
│ b[j][i] │
│ ↓ │
│ a[i][j] │
└───────┬───────┘

输出


二十九、给大家的最终口诀

🪄 “先横后竖,竖着不会排,就把竖变横;排完以后,再转回来!”

对应代码就是:

// ① 每行排序
sort(a[i] + 1, a[i] + m + 1);

// ② 转置
b[j][i] = a[i][j];

// ③ 再次用 sort 排行
sort(b[i] + 1, b[i] + n + 1);

// ④ 转置回来
a[i][j] = b[j][i];

这道题真正值得小学生学会的,不只是 sort(),也不只是“矩阵转置”,而是一个非常重要的编程思维:

当一个问题不好做的时候,不一定非要硬着头皮解决它;

有时候,把问题换一个方向,它就突然变成自己熟悉的问题了。

这里就是:

“不会排序列”

“把列变成行”

“我会排序行!”

sort()

这就是非常漂亮的算法转换思想。


赞(0)
未经允许不得转载:网硕互联帮助中心 » GESP2026年9月认证C++四级( 第三部分编程题(2、有序网络))精讲
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!