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] 设计巧妙,避免重复模拟,效率较高。

相关推荐
全栈凯哥16 分钟前
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
java·算法·leetcode·链表
全栈凯哥18 分钟前
Java详解LeetCode 热题 100(27):LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)详解
java·算法·leetcode·链表
SuperCandyXu23 分钟前
leetcode2368. 受限条件下可到达节点的数目-medium
数据结构·c++·算法·leetcode
Humbunklung39 分钟前
机器学习算法分类
算法·机器学习·分类
Ai多利1 小时前
深度学习登上Nature子刊!特征选择创新思路
人工智能·算法·计算机视觉·多模态·特征选择
lyh13441 小时前
【SpringBoot自动化部署方法】
数据结构
MSTcheng.2 小时前
【数据结构】顺序表和链表详解(下)
数据结构·链表
Q8137574602 小时前
中阳视角下的资产配置趋势分析与算法支持
算法
yvestine2 小时前
自然语言处理——文本表示
人工智能·python·算法·自然语言处理·文本表示
慢半拍iii2 小时前
数据结构——F/图
c语言·开发语言·数据结构·c++