CCF GESP202503 Grade4-B4263 [GESP202503 四级] 荒地开垦


📘题目描述:开垦荒地 传送门:https://www.luogu.com.cn/problem/B4263

小杨有一大片荒地,可以表示为一个 n n n 行 m m m 列的网格图。

小杨想要开垦这块荒地,但荒地中一些位置存在杂物,对于一块不存在杂物的荒地,该荒地可以开垦当且仅当其上下左右四个方向相邻的格子均不存在杂物

小杨可以选择至多一个位置 ,清除该位置的杂物,移除后该位置变为荒地。小杨想知道,在清除至多一个位置的杂物的情况下,最多能够开垦多少块荒地。


✅输入格式:

第一行包含两个正整数 n , m n, m n,m,含义如上所述。

之后 n n n 行,每行一个长度为 m m m 的字符串,仅包含 .#

  • . 表示荒地
  • # 表示杂物

✅输出格式:

输出一个整数,代表在清除至多一个杂物后,最多可以开垦的荒地块数。


🧪输入样例 #1

复制代码
3 5
.....
.#..#
.....

✅输出样例 #1

复制代码
11

📌样例说明:

原图:

复制代码
.....
.#..#
.....

清除中间那块杂物 mat[2][2] 后变为:

复制代码
.....
....#
.....

每一行均有多块可开垦地,最多为:

  • 第一行:前 4 块
  • 第二行:前 3 块
  • 第三行:前 4 块
    总共:11块荒地

这道题目是一个典型的模拟搜索+贪心类网格题,其关键在于判断某个荒地是否可以被开垦(满足四个方向没有杂物),以及在至多清除一个杂物的前提下,最多能获得多少"可开垦"的荒地数。


🧩 一、题目简述(问题理解)

给定一个大小为 n x m 的网格图:

  • . 表示"荒地"
  • # 表示"杂物"

定义"可开垦的荒地 ":一个 . 格子如果它的上下左右四个方向 都没有杂物,则该格子可以被开垦。

你可以选择清除最多一个 # 杂物 ,使得整体可以开垦的荒地数最大


🧮 二、解题思路分析

✅ Step 1:初始统计

  • 首先遍历每个格子:

    • 如果当前格子是 . 并且它的四个相邻格子中恰好有一个 #,那么去掉那个 # 就能使当前格子变得可开垦。
    • 如果本身四周无杂物,那它就已经是可开垦的。

a[i][j] 数组来辅助统计:表示如果清除了某个杂物,它能帮助多少个周围的荒地变得可开垦。


✅ Step 2:模拟清除一个杂物

  • 我们统计:对每一个杂物点 #,它若被清除,会让周围多少个点从"不可开垦"变成"可开垦"。
  • 记录在 a[i][j] 数组中。

✅ Step 3:寻找最优解

  • 原始能开垦的荒地数为 ans
  • 再从 a[i][j] 中找出最大的贡献值 mx
  • 答案即为:ans + mx

🧠 三、关键变量说明

变量 含义
mat[i][j] 原始地图字符矩阵
a[i][j] 杂物被清除后可能产生的额外可开垦荒地数量
ans 原本可开垦的荒地数(不移除杂物)
mx 清除某个杂物后,额外可以开垦的最大值
d[4][2] 表示方向向量,上下左右

💻C++ 题解代码(含注释)

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

const int N = 1005;
char mat[N][N];     // 地图
int a[N][N];        // 杂物清除后带来的增益
const int d[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上下左右

int main() {
    int n, m, ans = 0;
    scanf("%d%d", &n, &m);
    assert(1 <= n && n <= 1000);
    assert(1 <= m && m <= 1000);

    for (int i = 1; i <= n; i++)
        scanf("%s", mat[i] + 1);  // 输入地图,从下标1开始

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            int num = 0, p = -1;

            // 统计周围方向上有多少个 #
            for (int k = 0; k < 4; k++) {
                int ni = i + d[k][0];
                int nj = j + d[k][1];
                if (mat[ni][nj] == '#') {
                    num++;
                    p = k;
                }
            }

            if (mat[i][j] == '.' && num == 1) {
                // 这个荒地被一个杂物阻碍,记录这个杂物带来的贡献
                int pi = i + d[p][0];
                int pj = j + d[p][1];
                a[pi][pj]++;
            }
            else if (mat[i][j] == '.' && num == 0) {
                // 本身就可以开垦
                ans++;
            }
            else if (mat[i][j] == '#' && num == 0) {
                // 杂物点自身四周没杂物,也可能是一个潜在的清除点
                a[i][j]++;
            }
        }
    }

    // 找出最多能额外开垦的数量
    int mx = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= m; j++)
            mx = max(mx, a[i][j]);

    cout << ans + mx << endl;
    return 0;
}

🔍 四、C++ 代码逐行讲解

cpp 复制代码
const int N = 1005; // 最大网格尺寸
char mat[N][N];     // 地图输入
int a[N][N];        // 杂物点的贡献值记录
const int d[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 4个方向
cpp 复制代码
int main() {
    int n, m, ans = 0;
    scanf("%d%d", &n, &m); // 输入行列
    ...
cpp 复制代码
for (int i = 1; i <= n; i++) 
    scanf("%s", mat[i] + 1); // 注意下标从1开始,方便处理边界

✅ 统计原始的开垦数与杂物的贡献

cpp 复制代码
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++) {
        int num = 0, p = -1;
        for (int k = 0; k < 4; k++) {
            if (mat[i + d[k][0]][j + d[k][1]] == '#') {
                num++; // 四周杂物数量
                p = k;  // 记录杂物位置
            }
        }
        // 如果正中是 '.' 且四周仅有1个杂物,则将那个杂物点的贡献 +1
        if (mat[i][j] == '.' && num == 1)
            a[i + d[p][0]][j + d[p][1]]++;
        // 当前点是 '.' 且周围没有杂物,直接可开垦
        else if (mat[i][j] == '.' && num == 0)
            ans++;
        // 当前是 '#' 且不影响任何点,也可以作为未来考虑清除的点
        else if (mat[i][j] == '#' && num == 0)
            a[i][j]++;
    }

✅ 找最大贡献值

cpp 复制代码
int mx = 0;
for (int i = 1; i <= n; i++)
    for (int j = 0; j <= m; j++)
        mx = max(mx, a[i][j]);

✅ 输出最终答案

cpp 复制代码
cout << ans + mx << endl;

🧪 五、样例解释

输入:

复制代码
3 5
.....
.#..#
.....

初始没有清除杂物时,能开垦:

  • 第一行 4 个格子(除了右边一个有相邻的 #
  • 第二行 2 个格子
  • 第三行 4 个格子
    共:10个

清除 (2,2) 的 # 后,第二行从 .#..# 变为 ....#

则第二行中间的点也变得可开垦,提升了 1 个

最终为 11


🧾 六、总结

  • 本题考查对网格搜索、状态统计、贪心选择的理解。
  • 注意细节如:不能重复计算,清除杂物仅限一次。
  • 辅助数组 a[i][j] 设计巧妙,避免重复模拟,效率较高。

相关推荐
listhi52013 小时前
基于梯度下降、随机梯度下降和牛顿法的逻辑回归MATLAB实现
算法·matlab·逻辑回归
熊猫_豆豆13 小时前
目前顶尖AI所用算法,包含的数学内容,详细列举
人工智能·算法
野犬寒鸦13 小时前
从零起步学习Redis || 第二章:Redis中数据类型的深层剖析讲解(下)
java·redis·后端·算法·哈希算法
java1234_小锋13 小时前
Scikit-learn Python机器学习 - 回归分析算法 - 弹性网络 (Elastic-Net)
python·算法·机器学习
hn小菜鸡14 小时前
LeetCode 2570.合并两个二维数组-求和法
数据结构·算法·leetcode
hn小菜鸡14 小时前
LeetCode 524.通过删除字母匹配到字典里最长单词
算法·leetcode·职场和发展
Greedy Alg14 小时前
LeetCode 226. 翻转二叉树
算法
我要成为c嘎嘎大王14 小时前
【C++】模版专题
c++·算法
jndingxin14 小时前
算法面试(5)------NMS(非极大值抑制)原理 Soft-NMS、DIoU-NMS 是什么?
人工智能·算法·目标跟踪
苏纪云15 小时前
算法<java>——排序(冒泡、插入、选择、归并、快速、计数、堆、桶、基数)
java·开发语言·算法