双端队列广搜

解决问题:

双端队列主要解决图中边的权值只有 或者 的最短路问题

操作:

每次从队头取出元素,并进行拓展其他元素时

  1. 若拓展某一元素的边权是 0,则将该元素插入到队头
  2. 若拓展某一元素的边权是 1,则将该元素插入到队尾

与堆优化Dijkstra 一样,必须在出队时才知道每个点最终的最小值,而和一般的bfs不一样,原因是如下图所示

解释:当我们第一次用1号点更新3号点的时候,3号点当前的最短路距离并不是真正的最短距离,并且3号点会放在队尾,会被2号点再次更新,放在队头,当3号点出队的时候,就是真正的最短距离了。(其实看做dijkstra算法就好了)

双端队列广搜求最短路的合法性:

在通常的广搜 bfs 中,边权都为 ,这样每次被更新时,新加入队列的点一定更新它的点的 dist 要大,这样就可以满足bfs求最短路的两个条件:

  1. 两段性
  2. 单调性

但在这里,由于边权可能为 (不需要旋转边时),所以说我们不能总是把被更新的点加入队尾,因为如果这个点是被权值为 0 的边更新,那么它的 dist 是可能比队尾的 dist 小的,所以说此时要把该点加入对头,这样就满足了两段性,也就满足了单调性。

deque 就满足了既要加入对头,又加入队尾的操作。


 175. 电路维修 - AcWing题库

关于题目: 

首先明确的是,图中的格子和点是不一样的,题目的输入是格子(或者是边),而我们要找的是从起点 (0,0) 到终点 (n,m) 的一条最短路

 

按左上角,右上角,右下角,左下角遍历的顺序

  1. dx[] 和 dy[] 表示可以去其他点的方向
  2. ix[] 和 iy[] 表示需要踩某个方向的边才能取到相应的点
  3. ch[] 表示当前点走到 个方向的点理想状态下格子形状(边权是 的状态)

没有答案的情况

由于我们的每条边都是斜边(题目说明没有横向边和竖向边),那么我们每次走过一条过,和 的值要么都 +1,要么都 -1。又因为起点为(0,0),所以说,我们每次到达的点的横纵坐标之和为偶数,即 x+y&1=0 ,所有横纵坐标之和为奇数的点我们都无法走到!

时间复杂度 O(nm)

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

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 550;

char ch[] = {'\\', '/', '\\', '/'};//对应的边,注意\是转义字符,表示\需要两个'\''
//char ch[] = "\\/\\/";也可以这样写,注意ch字符的结尾会有一个'\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};//找连接到这个点的边
int T, n, m;
int dist[N][N];
char g[N][N];
bool st[N][N];

int bfs()
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, false, sizeof st);
    deque<PII> q;
    
    q.push_front({0, 0});//放入起点
    dist[0][0] = 0;//dist[起点] = 0
    
    while(q.size())
    {
        auto t = q.front();//去队头(最小值)
        q.pop_front();//出队
        
        int a = t.x, b = t.y;
        int distance = dist[a][b];
        
        if(a == n && b == m)    return dist[n][m];//如果到达终点,返回
        
        if(st[a][b])    continue;//如果被访问过
        st[a][b] = true;
        
        for(int i = 0; i < 4; i ++ )
        {
            int pa = a + dx[i], pb = b + dy[i];//下一个要访问的点
            if(pa < 0 || pa > n || pb < 0 || pb > m)   continue;//注意x和y可以取到n,m
            
            int ea = a + ix[i], eb = b + iy[i];  //与访问点相连的边
            int d = distance + (g[ea][eb] != ch[i]);
            if(dist[pa][pb] > d)
            {
                dist[pa][pb] = d;
                if(g[ea][eb] == ch[i])  q.push_front({pa, pb});
                else    q.push_back({pa, pb});
            }
        }
    }
    
    return -1;//一定不会取到
}

int main()
{
    cin >> T;
    while(T -- )
    {
        cin >> n >> m;
        for(int i = 0; i < n; i ++ )
            for(int j = 0; j < m; j ++ )
                cin >> g[i][j];
        if(n + m & 1)   puts("NO SOLUTION");
        else    printf("%d\n", bfs());
    }

    return 0;
}

posted @ 2022-05-05 08:41  光風霽月  阅读(31)  评论(0编辑  收藏  举报