[补题记录] StarryCoding 入门教育赛3 D.电弧陷阱

URL:小白教育赛3

题目描述

e e e和桶子在打派的时候被一个艾许的电弧陷阱控在原地动弹不得,于是他们想办法找出哪些地方是可以去的,哪些地方是不能去的。

给定一个由字符构成的 N ∗ M N * M NM的矩阵,其中包括:

  • . 表示这是一块空地;
  • # 表示这是一个电弧陷阱;

电弧陷阱会使得它的上下左右四个方向充满电弧,走到这些地方就会被困住,不能再行动。

他们想知道如果从一个地方出发,最多可以到达多少个地方?

注意:数据保证至少有一个空地。

输入格式

第一行输入 N N N M M M

接下来 N N N行,每行输入 M M M个字符。

1 ≤ N , M ≤ 1000 1 \le N, M \le 1000 1N,M1000

输出格式

输出一个整数表示答案。

输入样例1

3 5
.#...
.....
.#..#

输出样例1

9

输入样例2

5 3
#.#
...
#.#
...
...

输出样例2

7

思路

类似这种给一个矩阵,然后有些位置不能去、被挡住、有传送门之类的,一般都需要用到 b f s bfs bfs。这种题就类似于走迷宫,需要保证走过的点不要再走,否则会让时间复杂度变高。

显然电弧陷阱会将整个矩阵分成好几部分连通区域,我们只需要在遍历矩阵的时候判断一下当前位置,是否可以作为出发点即可开始 b f s bfs bfs

此时一个连通部分的答案 = . 的数量 + 电弧数量。

. 的数量我们可以通过找到所有不会被电弧困住的地方来计算,而难点在于如何算出 电弧 的数量。

最简单的方法就是使用set维护一个pair<x, y>直接去重,最后加上 set.size() 即可。

容易出错的地方在于,如果选择将 # 周围的点修改为其他字符做标记,那么一定要在 输入完成后进行bfs之前 进行标记。

还需要注意,题目保证了至少一个点为 . ,因此答案至少为 1。

代码

#include "bits/stdc++.h"

char mp[1007][1007];

int vis[1007][1007], n, m, ans;

const int dir[] = {0, 1, 0, -1, 0};

bool check_boom(int x, int y) { // 检查 {x, y} 是否有电弧
    for (int i = 0; i < 4; ++ i) {
        int nx = x + dir[i], ny = y + dir[i + 1];
        if (mp[nx][ny] == '#') return false;
    }
    return true;
}

bool check(int x, int y) {
    return x >= 1 and x <= n and y >= 1 and y <= m and mp[x][y] != '#' and check_boom(x, y) and vis[x][y] == 0;
}

void bfs(int x, int y) {
    // . and 电弧
    std::set<std::pair<int, int>> set_a;
    std::set<std::pair<int, int>> set_b;
    // now {x, y}
    std::queue <std::pair<int, int>> q;
    q.push({x, y});

    while (!q.empty()) {
        auto top = q.front(); q.pop();

        set_a.insert(top);

        for (int i = 0; i < 4; ++ i) {
            int nx = top.first + dir[i], ny = top.second + dir[i + 1]; // next_x, next_y
            if (check(nx, ny)) {
                q.push({nx, ny});
                vis[nx][ny] = 1;
            }
        }
    }

    // 遍历 . 的 set,检查其周围是否有 电弧
    for (auto &p : set_a) {
        for (int i = 0; i < 4; ++ i) {
            int nx = p.first + dir[i], ny = p.second + dir[i + 1];
            if (mp[nx][ny] == '.' and vis[nx][ny] == 0) 
                set_b.insert({nx, ny});
        }
    }   

    int num = set_a.size() + set_b.size();    
    ans = std::max(ans, num);
}

signed main() {
    std::cin >> n >> m;
    for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= m; ++ j) {
            std::cin >> mp[i][j];
        }
    }
    for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= m; ++ j) {
            if (vis[i][j] == 0 and mp[i][j] == '.') {
                ans = std::max(1, ans); // 只要有一个位置为 . ,答案就至少为1
                if (check_boom(i, j)) bfs(i, j);
            }
        }
    }
    std::cout << ans;
}

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值