



🌈 一、故事开场:小画家小 A 的烦恼
1、小 A 是个小画家,他画了一幅画在一个大大的画布上。
画布是一个:
-
高 n 行
-
宽 m 列
2、就像一个 n × m 的字符方格地图。
比如:
…..
.>_<.
…..
但是呢……
小 A 发现画的主体只有中间那一小块,四周全是空白。
3、他的要求:
“我要把画布裁剪一下,只保留我要的那一块!”
4、于是我们:
-
从第 x1 行 到 第 x2 行
-
从第 y1 列 到 第 y2 列
把这一块剪下来打印出来。
🧠 二、先理解题目本质
1、其实这题在考什么?
👉 本质就是:
从二维字符矩阵中,截取一个子矩阵并输出
2、是不是很像:
-
取数组的一部分?
-
二维数组的局部输出?
3、这题核心就是:
✅ 二维数组的范围控制
📦 三、我们一步一步拆解思考过程
1、🎯 第一步:怎么存画布?
(1)题目说:
接下来 n 行,每行一个长度为 m 的字符串。
所以我们可以这样存:
char s[105][105];
(2)或者用 string:
string s[105];
(3)为什么是 105?
因为题目最大 n,m ≤ 100
多给一点空间,安全!
2、🎯 第二步:如何理解“裁剪”?
(1)假设:
x1 = 2
x2 = 3
y1 = 2
y2 = 4
(2)意思是:
-
行:只看第2行到第3行
-
列:只看第2列到第4列
(3)就像从一个大地图里:
📍 切出一个小矩形
3、✂ 裁剪本质是什么?
我们只需要:
for (int i = x1; i <= x2; i++)
for (int j = y1; j <= y2; j++)
输出 s[i][j]
是不是超级清楚?
🧠 四、关键算法思考全过程
1、很多同学看到题目会慌:
“这是不是要用什么高级算法?”
2、不要谎!我们可以逐个考点分析:
1️⃣ 有排序吗?
没有
2️⃣ 有搜索吗?
没有
3️⃣ 有递推吗?
没有
4️⃣ 有复杂数学吗?
没有
3、那它到底考什么?
👉 二维数组读入 + 双重循环控制范围
🎯 五、完整思考路径总结
我们做编写程序时,思路这样走:
1、🪜 第一步:读入参数
cin >> n >> m;
cin >> x1 >> x2 >> y1 >> y2;
2、🪜 第二步:读入画布
for (int i = 1; i <= n; i++)
cin >> s[i];
3、🪜 第三步:控制行范围
for (int i = x1; i <= x2; i++)
4、🪜 第四步:控制列范围
for (int j = y1; j <= y2; j++)
5、🪜 第五步:输出并换行
每输出完一行要换行!
💻 六、标准代码
1、参考代码:
#include <iostream>
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
int x1, x2, y1, y2;
cin >> x1 >> x2 >> y1 >> y2;
string s[105];
for(int i = 1; i <= n; i++)
{
cin >> s[i];
}
for(int i = x1; i <= x2; i++)
{
for(int j = y1; j <= y2; j++)
{
cout << s[i][j-1];
}
cout << endl;
}
return 0;
}
⚠ 注意:
-
string 下标从 0 开始
-
题目行列是从 1 开始
所以列要写 j-1
2、参考代码:
#include <cstdio>
using namespace std;
const int N = 105;
int n, m;
char s[N][N];
int x1, x2, y1, y2;
int main() {
scanf("%d%d", &n, &m);
scanf("%d%d%d%d", &x1, &x2, &y1, &y2);
for (int i = 1; i <= n; i++)
scanf("%s", s[i] + 1);
for (int i = x1; i <= x2; i++) {
s[i][y2 + 1] = 0;
printf("%s\\n", s[i] + y1);
}
return 0;
}
⏱ 七、时间复杂度分析
1、假设:
-
n 最大 100
-
m 最大 100
2、最多输出:
100 × 100 = 10000 次
时间复杂度:
O(n × m)
🌟 八、这题考点
| 二维数组 | ✅ |
| 双重循环 | ✅ |
| 下标控制 | ✅ |
| 细节严谨 | ✅ |
| 复杂算法 | ❌ |
🚨 九、最容易错的地方
❌ 错误1:忘记 j-1
会导致:
-
输出错位
-
或 RE 错误
❌ 错误2:循环写成 < 而不是 <=
少输出一行或一列。
❌ 错误3:行列顺序写反
记住:
先控制行
再控制列
🎉 十、这题的等级定位
1、这题属于:
四级里的“送分题”
2、它考察:
-
你是否会基本数组操作
-
是否能认真控制边界。
🏁 最后总结一句话
🎨《画布裁剪》的本质是:
在二维字符矩阵中,按给定范围输出子矩阵。
核心能力:
-
二维数组理解
-
下标细节控制
-
双重循环结构
预测模拟题:
🎯《升级版裁剪(自动寻找非空白区域)》
🌈 一、题目升级版
1、小 A 又画了一幅画。
这次他没有告诉你:
-
从第几行到第几行
-
从第几列到第几列
2、他只说了一句话:
“帮我把四周的空白都裁掉,只留下画的主体!”
3、这下麻烦了……
我们要 自动找出画的主体在哪儿!
🧠 二、题目真正考什么?
升级题其实在考:
✅ 二维数组遍历
✅ 找最小矩形包围区域
✅ 边界更新思维
这才是四级考生应该有的水平了!
🎨 三、什么叫“非空白区域”?
1、假设画布是:
…..
..#..
..#..
…..
2、这里:
. 是空白
# 是画的内容
3、我们要裁剪成:
##
##
4、其实就是找出:
-
最上面的非空白行
-
最下面的非空白行
-
最左边的非空白列
-
最右边的非空白列
🧩 四、核心算法思考过程
我们一步一步思考:
1、🪜 第一步:我们要找什么?
我们需要 4 个变量:
top = 最小行号
bottom = 最大行号
left = 最小列号
right = 最大列号
2、🪜 第二步:怎么找到它们?
我们遍历整个画布!
for 每一行
for 每一列
如果不是空白
更新边界
3、🔥 关键算法思想:动态更新边界
假设当前坐标是 (i, j),发现是画的一部分:
我们就:
top = min(top, i);
bottom = max(bottom, i);
left = min(left, j);
right = max(right, j);
🧠 五、完整思考流程
🎯 1️⃣ 初始化
一开始我们不知道主体在哪。
所以:
top = n
bottom = 1
left = m
right = 1
🎯 2️⃣ 扫描全图
一格一格找。
只要发现不是 . 的字符:
就更新边界。
🎯 3️⃣ 最后得到一个矩形
就是:
(top, left) 到 (bottom, right)
🎯 4️⃣ 输出这个矩形
和原题一样:
双重循环输出,或者格式化输出。
💻 六、完整代码
#include <iostream>
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
string s[105];
for(int i = 0; i < n; i++)
{
cin >> s[i];
}
int top = n, bottom = -1;
int left = m, right = -1;
// 扫描全图
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(s[i][j] != '.') // 非空白
{
if(i < top) top = i;
if(i > bottom) bottom = i;
if(j < left) left = j;
if(j > right) right = j;
}
}
}
// 输出裁剪结果
for(int i = top; i <= bottom; i++)
{
for(int j = left; j <= right; j++)
{
cout << s[i][j];
}
cout << endl;
}
return 0;
}
⏱ 七、时间复杂度分析
1、扫描一次全图:
O(n × m)
2、输出最多也是:
O(n × m)
3、总复杂度:
O(n × m)
非常安全。
🚀 八、算法思想升华
1、这题其实是:
找二维数组中满足条件的最小包围矩形
2、这种思想以后会用在:
-
图像处理
-
游戏地图裁剪
-
连通块问题
-
DFS 扫描
🌟 九、常见错误
❌ 忘记初始化为极值
❌ 行列搞反
❌ 没处理“全空白”的情况
如果全是 . 呢?
那就什么都不输出,或者按照考试说明输出。
🎓 十、和原题基础版的区别
| 给定范围 | 自动寻找范围 |
| 直接输出 | 先扫描再输出 |
| 简单循环 | 带边界更新 |
🏁 最终总结一句话
🎯 升级版裁剪的核心是:
在二维数组中,扫描所有元素,动态维护最小包围矩形。






