[bfs] aw175. 电路维修(双端队列广搜+dijkstra理解+好题+难题)

12 篇文章 0 订阅

1. 题目来源

链接:175. 电路维修

推荐题解!!!小呆呆大佬,详细解释

2. 题目解析

怎么说呢,看了半天才看懂…

双端队列广搜主要解决边权只有 0,1 两种情况时最短路问题

看到大佬的题解,自己貌似没那么迷了,推荐题解!!!小呆呆大佬,详细解释

bfs 保证了两段性、单调性就能够搜到最短路(详情可参考算导中的证明)。 双端队列广搜就是应用这一特点,将边权为 0 的加到队头,边权为 1 的加到队尾,依旧满足两段性、单调性!

实质上本题和 dijkstra 算法大同小异,用双端队列边权只有 0 1 这两种情况来代替了 dijkstra 的小顶堆。

也是由于其边权值不统一,在更新其他点入队的时候,并不是最小值,而只有在出队的时候才是最小值。这点在 推荐题解!!!小呆呆大佬,详细解释 讲的很清楚!

真是个神奇的算法!


时间复杂度: O ( n ) O(n) O(n)

空间复杂度: O ( n ) O(n) O(n)


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <deque>

using namespace std;

typedef pair<int, int> PII;

const int N = 505;

int n, m;
char g[N][N];
int dist[N][N];
bool st[N][N];      // 防止一个点的多次更新,和 dijkstra 一样,队列中会有冗余元素

int bfs() {
    deque<PII> q;
    memset(st, 0, sizeof st);
    memset(dist, 0x3f, sizeof dist);
    q.push_front({0, 0});
    dist[0][0] = 0;

    // 存四方向上对应的通路是多少
    char cs[5] = "\\/\\/";      // 点到四方向斜线的通路情况,注意需要开 5 大小,包含末尾 \0
    // 四方向上的临点
    int dx[4] = {-1, -1, 1, 1}, dy[4] = {-1, 1, 1, -1};
    // 求四方向边是多少,需要推导坐标关系
    int ix[4] = {-1, -1, 0, 0}, iy[4] = {-1, 0, 0, -1};

    while (q.size()) {
        auto t = q.front(); q.pop_front();

        int x = t.first, y = t.second;      // 出队的点的距离是最短的,用它来更新其它点到它的距离

        // 在出队的时候判断是否到终点,返回最小值。入队的时候不一定为最小值,不要进行误判
        if (x == n && y == m) return dist[x][y];    

        // 和 dijkstra 一样的判重
        // 如果为 true 说明已经出过队了,最小值确定,队列中的是冗余,不用管它
        // 否则说明这是个新确定最短路的点,需要用其来更新其它临点
        if (st[x][y]) continue;             
        st[x][y] = true;

        for (int i = 0; i < 4; i ++ ) {
            int a = x + dx[i], b = y + dy[i];
            if (a < 0 || a > n || b < 0 || b > m) continue;     // 左上角是(0,0),右下角为(n,m)即点能取到 n, m
            int ga = x + ix[i], gb = y + iy[i];
            int w = (g[ga][gb] != cs[i]);       // 如果与该方向上斜线相反,需要旋转,边权为 1 
            int d = dist[x][y] + w;           
            if (d < dist[a][b]) {
                dist[a][b] = d;
                if (w) q.push_back({a, b});
                else q.push_front({a, b});
            }

        }
    }

    return -1;
}

int main() {
    int T;
    scanf("%d", &T);

    while (T -- ) {
        scanf("%d%d", &n, &m);

        for (int i = 0; i < n; i ++ ) scanf("%s", g[i]);

        if (n + m & 1) puts("NO SOLUTION");
        else printf("%d\n", bfs());
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值