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

  • @ 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],满足则计数。

    引导性问题

    1. 题目中矩阵的元素是整数。你的判断条件 a*d == b*c 在数学上是“二阶矩阵行列式为零”的等价形式,这在整数运算下完全正确吗?考虑一下:如果 a, b, c, d 都在 int 范围内,那么 a*db*c 的结果可能超出 int 范围,导致溢出,比较结果出错。数据范围是多少?你检查过题目对矩阵元素数值的限制吗?
    2. 即使没有溢出,这个枚举算法的时间复杂度是 O(n*m),对于 n, m <= 500 的数据范围是完全可以接受的。这是这道题预期的解法。

    关键点提醒:在写题解���,必须强调数据类型的选取。如果元素值可能较大(例如达到 10^5,乘积就会达到 10^10,超出 int 范围),应该使用 long long 来存储乘积并进行比较。这是竞赛中非常常见的易错点。

    B. 画布裁剪

    你的思路:直接读入整个画布,然后根据给定的坐标范围 (x1, y1)(x2, y2) 输出子矩阵。

    引导性问题

    1. 这道题逻辑很直接。但请注意你的输入部分:cin >> g[i] + 1。这是将字符串读入从 g[i][1] 开始的地址。这要求数组 g 的第二维大小(即 N)必须足够大,能容纳字符串和一个额外的起始偏移。你定义的 N=110 是足够的。但你需要理解这种写法的原理和风险。
    2. 题目给出的坐标 x1, x2, y1, y2 是从1开始计数的吗?你的代码假设了这一点。如果题目说“第x行”,通常就是从1开始。这是一个需要仔细读题确认的地方。
    3. 有没有更节省空间的方法?如果 hw 非常大(比如 10^5),但裁剪的区域很小,我们还需要存储整个画布吗?可以思考一种流式处理的方法:只读入需要的行,并在读入时直接裁剪列。这对于本题数据范围 (N=100) 不是必须的,但是一种有价值的优化思路。

    C. 荒地开垦

    你的思路

    1. 先枚举每一个 #(石头),尝试将其移除(变为 .)。
    2. 移除后,立即检查这个位置及其上下左右四个相邻位置,调用 check 函数判断它们是否变成“可开垦荒地”(即自身是 . 且四邻都不是 #)。记录本次移除能新增的“可开垦荒地”数量,并更新最大值 res
    3. 最后,再遍历一遍整个地图,统计初始状态下(不进行任何移除)就已经是“可开垦荒地”的数量,加到 res 上。

    这是本题的核心,我们需要重点分析:

    1. 时间复杂度:你的算法最坏情况下,对于每个 #(最多 n*m 个),进行5次 check 操作(每次 check 是 O(1) 的)。所以是 O(n*m),对于 n, m <= 1000 是可行的。
    2. 思路正确性
      • 关键点在于:移除一个石头后,可能新产生的“可开垦荒地”只可能位于这个石头的位置,以及这个石头紧邻的四个格子。因为 check 条件要求该格子周围四个格子都不能有 #。移除一个石头,只会影响它自身和它邻居的“周围环境”。
      • 你的枚举覆盖了所有可能性(移除每个石头),并对每个可能移除的位置,检查了所有可能受益的格子(自身和四邻)。这个思路是完备的。
    3. 潜在问题与改进
      • check 函数的边界:你的 check 函数中,if (g[a][b] == '#') 没有检查 ab 是否越界(即 a, b 是否在 [1, n][1, m] 范围内)。当 (x, y) 是地图边缘的格子时,访问 dx, dy 会导致越界。这是一个严重的BUG。在竞赛中,处理网格图搜索时,务必首先判断坐标合法性
      • 逻辑重复:在最后统计初始荒地时,你又遍历并调用了 check(i, j)。这部分荒地,在之前枚举移除石头时,并没有被破坏(因为你的枚举是“尝试性”的,每次都会把石头改回来)。所以最后加上是合理的。但你可以思考,能否在一次遍历中就完成所有计算?例如,先预处理出每个 . 格子周围 # 的数量?
      • 更清晰的逻辑框架:可以这样向其他同学解释:

        本题的突破口是发现“可开垦荒地”的判断是局部的。移除一个石头,只会改变以它为中心的5个格子的状态。因此,我们只需模拟移除每一个石头,并快速计算这次操作能带来多少新的可开垦格子(这5个格子中,有多少从“不可垦”变成了“可垦”)。同时,别忘了加上一开始就可垦的格子。计算“变化”时,需要小心处理,避免重复计算一个格子因为多个石头移除而多次被计入。

    给你的行动建议

    1. 请首先修复C题代码中的数组越界问题。在 check 函数中,在访问 g[a][b] 之前,先判断 ab 是否在有效范围内。
    2. 思考一下,如果题目数据范围扩大到 n, m <= 2000,你的算法依然可行吗?(O(n*m) 约为 4e6,仍然可行)。
    3. 尝试为A题添加一句关于“使用 long long 防止溢出”的说明,这体现了你的严谨。
    4. 为B题思考一下我提到的“流式处理”伪代码,这能提升你对问题的理解。

    如果你修改了代码,或者对上述分析有疑问,可以继续回复。记住,理解算法为什么正确,比写出能AC的代码更重要。

    • 1