- 题解
【题解】小测
- @ 2026-2-9 12:16:11
A. 二阶矩阵
#include <bits/stdc++.h>
using namespace std;
const int N = 510;
int n, m;
int w[N][N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
cin >> w[i][j];
int res = 0;
for (int i = 1; i < n; i ++ )
for (int j = 1; j < m; j ++ )
if (w[i][j] * w[i + 1][j + 1] == w[i + 1][j] * w[i][j + 1])
res ++ ;
cout << res << endl;
return 0;
}
B. 画布裁剪
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int h, w;
char g[N][N];
int main()
{
cin >> h >> w;
int x1, x2, y1, y2;
cin >> x1 >> x2 >> y1 >> y2;
for (int i = 1; i <= h; i ++ ) cin >> g[i] + 1;
for (int i = x1; i <= x2; i ++ )
{
for (int j = y1; j <= y2; j ++ )
cout << g[i][j];
cout << endl;
}
return 0;
}
C. 荒地开垦
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m;
char g[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
bool check(int x, int y) // 判断是不是可开垦荒地
{
if (g[x][y] != '.') return false;
for (int i = 0; i < 4; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (g[a][b] == '#') return false;
}
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> g[i] + 1;
int res = 0;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
if (g[i][j] == '#')
{
g[i][j] = '.';
int cnt = 0;
if (check(i, j)) cnt ++ ;
for (int k = 0; k < 4; k ++ )
{
int a = i + dx[k], b = j + dy[k];
if (check(a, b)) cnt ++ ;
}
res = max(res, cnt);
g[i][j] = '#';
}
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
if (check(i, j))
res ++ ;
cout << res << endl;
return 0;
}
1 comments
-
Myobot @ 2026-2-9 12:17:02
同学你好,我是信息竞赛教练。
首先,感谢你分享这三道题的代码。作为教练,我的职责是引导你深入理解算法本质,而不仅仅是提供可运行的代码。因此,我不会直接评价你的代码“对”或“错”,而是通过提问和引导,帮助你检验自己的思路,发现潜在问题,并理解更优的解法。
让我们逐一分析:
A. 二阶矩阵
你的思路:枚举所有左上角为
(i, j)的 2x2 子矩阵,检查是否满足w[i][j] * w[i+1][j+1] == w[i+1][j] * w[i][j+1],满足则计数。引导性问题:
- 题目中矩阵的元素是整数。你的判断条件
a*d == b*c在数学上是“二阶矩阵行列式为零”的等价形式,这在整数运算下完全正确吗?考虑一下:如果a, b, c, d都在int范围内,那么a*d和b*c的结果可能超出int范围,导致溢出,比较结果出错。数据范围是多少?你检查过题目对矩阵元素数值的限制吗? - 即使没有溢出,这个枚举算法的时间复杂度是
O(n*m),对于n, m <= 500的数据范围是完全可以接受的。这是这道题预期的解法。
关键点提醒:在写题解���,必须强调数据类型的选取。如果元素值可能较大(例如达到
10^5,乘积就会达到10^10,超出int范围),应该使用long long来存储乘积并进行比较。这是竞赛中非常常见的易错点。B. 画布裁剪
你的思路:直接读入整个画布,然后根据给定的坐标范围
(x1, y1)到(x2, y2)输出子矩阵。引导性问题:
- 这道题逻辑很直接。但请注意你的输入部分:
cin >> g[i] + 1。这是将字符串读入从g[i][1]开始的地址。这要求数组g的第二维大小(即N)必须足够大,能容纳字符串和一个额外的起始偏移。你定义的N=110是足够的。但你需要理解这种写法的原理和风险。 - 题目给出的坐标
x1, x2, y1, y2是从1开始计数的吗?你的代码假设了这一点。如果题目说“第x行”,通常就是从1开始。这是一个需要仔细读题确认的地方。 - 有没有更节省空间的方法?如果
h和w非常大(比如10^5),但裁剪的区域很小,我们还需要存储整个画布吗?可以思考一种流式处理的方法:只读入需要的行,并在读入时直接裁剪列。这对于本题数据范围 (N=100) 不是必须的,但是一种有价值的优化思路。
C. 荒地开垦
你的思路:
- 先枚举每一个
#(石头),尝试将其移除(变为.)。 - 移除后,立即检查这个位置及其上下左右四个相邻位置,调用
check函数判断它们是否变成“可开垦荒地”(即自身是.且四邻都不是#)。记录本次移除能新增的“可开垦荒地”数量,并更新最大值res。 - 最后,再遍历一遍整个地图,统计初始状态下(不进行任何移除)就已经是“可开垦荒地”的数量,加到
res上。
这是本题的核心,我们需要重点分析:
- 时间复杂度:你的算法最坏情况下,对于每个
#(最多n*m个),进行5次check操作(每次check是 O(1) 的)。所以是O(n*m),对于n, m <= 1000是可行的。 - 思路正确性:
- 关键点在于:移除一个石头后,可能新产生的“可开垦荒地”只可能位于这个石头的位置,以及这个石头紧邻的四个格子。因为
check条件要求该格子周围四个格子都不能有#。移除一个石头,只会影响它自身和它邻居的“周围环境”。 - 你的枚举覆盖了所有可能性(移除每个石头),并对每个可能移除的位置,检查了所有可能受益的格子(自身和四邻)。这个思路是完备的。
- 关键点在于:移除一个石头后,可能新产生的“可开垦荒地”只可能位于这个石头的位置,以及这个石头紧邻的四个格子。因为
- 潜在问题与改进:
check函数的边界:你的check函数中,if (g[a][b] == '#')没有检查a和b是否越界(即a, b是否在[1, n]和[1, m]范围内)。当(x, y)是地图边缘的格子时,访问dx, dy会导致越界。这是一个严重的BUG。在竞赛中,处理网格图搜索时,务必首先判断坐标合法性。- 逻辑重复:在最后统计初始荒地时,你又遍历并调用了
check(i, j)。这部分荒地,在之前枚举移除石头时,并没有被破坏(因为你的枚举是“尝试性”的,每次都会把石头改回来)。所以最后加上是合理的。但你可以思考,能否在一次遍历中就完成所有计算?例如,先预处理出每个.格子周围#的数量? - 更清晰的逻辑框架:可以这样向其他同学解释:
本题的突破口是发现“可开垦荒地”的判断是局部的。移除一个石头,只会改变以它为中心的5个格子的状态。因此,我们只需模拟移除每一个石头,并快速计算这次操作能带来多少新的可开垦格子(这5个格子中,有多少从“不可垦”变成了“可垦”)。同时,别忘了加上一开始就可垦的格子。计算“变化”时,需要小心处理,避免重复计算一个格子因为多个石头移除而多次被计入。
给你的行动建议:
- 请首先修复C题代码中的数组越界问题。在
check函数中,在访问g[a][b]之前,先判断a和b是否在有效范围内。 - 思考一下,如果题目数据范围扩大到
n, m <= 2000,你的算法依然可行吗?(O(n*m) 约为 4e6,仍然可行)。 - 尝试为A题添加一句关于“使用
long long防止溢出”的说明,这体现了你的严谨。 - 为B题思考一下我提到的“流式处理”伪代码,这能提升你对问题的理解。
如果你修改了代码,或者对上述分析有疑问,可以继续回复。记住,理解算法为什么正确,比写出能AC的代码更重要。
- 题目中矩阵的元素是整数。你的判断条件
- 1