【搜索专题】BFS中的多源BFS-双端队列BFS

A、AcWing 173. 矩阵距离(多源BFS)

在这里插入图片描述

所有点到多个终点的最短距离 我们可以建一个虚拟源点,虚拟源点到所有终点连0权的边,跑一次单源最短路即可 答案就是每一个点到虚拟源点的最近距离

先将dist全部置为-1,可以用来判断是否经过

然后直接将所有起点(值为1)dist都置为0并且全部入队跑一遍bfs即可,利用了一个虚拟源点,只不过不用建出来这个源点

两段性 = > => => 单调性,主要是单调性起作用
选好起点直接跑bfs就会更新所有的未更新的结点
每次入队最后出队的时候就已经是是最小值

bfs这里每一个点的值只会更新一次

tt = -1 ++ tt hh <= tt

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define x first
#define y second 
using namespace std;
typedef pair<int, int > PII ; 
const int N = 1007, M = N * N ;

int dist[N][N];
PII q[M];
int n, m;
char g[N][N];

void bfs(){
    const int dx[4] = {-1, 0, 1, 0};
    const int dy[4] = {0, 1, 0, -1};
    
    memset(dist, -1, sizeof dist);
    
    int hh = 0, tt = -1;
    
    for(int i = 0;i < n;++i)
        for(int j = 0;j < m;++j)
            if(g[i][j] == '1')
                q[++ tt ] = {i, j}, dist[i][j] = 0;
    while(hh <= tt){

        PII t = q[hh ++ ];
        if(hh == M)hh = 0;
 
        for(int i = 0;i < 4;i ++ ){
            int a = t.x + dx[i], b = t.y + dy[i];
            if(a == t.x && b == t.y)continue;
            if(a < 0 || b < 0 || a >= n || b >= m)continue;
            if(dist[a][b] != -1)continue;
            
            dist[a][b] = dist[t.x][t.y] + 1;
            q[++ tt ] = {a, b};
            if(tt == M)tt = 0;
        }
        
    }
}

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 0;i < n;++i)
    scanf("%s", g[i]);
    
    bfs();
    
    for(int i = 0;i < n;++ i){
        for(int j = 0;j < m; ++ j){
            printf("%d ", dist[i][j]);
        }
        puts("");
    }
    return 0; 
}

B、AcWing 1107. 魔板(最小步数模型)

在这里插入图片描述
最小步数模型 – 八数码

按照字典序输出一般按照正常写输出的就是字典序最小
先A再B再C的顺序来扩展到每一个状态一定可以得到字典序最小的解
我感觉是能同步到达只有前面不一样,最终到达的状态是一样的那么前面先选的是字典序小的最后字典序一定最小

相当于把整个棋盘作为一个点,有 三种操作,到达最终的点

一般存状态可以直接hash/map/unordered_map/康托展开

模块化编程

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <unordered_map>
using namespace std;

const int N = 5007;
int n, m;
char g[2][4];
//可以把字符串hash成一个数使用数组或者直接用map映射
unordered_map<string, int>dist;
unordered_map<string, pair<char, string> >pre;

string start, ed;

void set(string s){
    for(int i = 0;i < 4; ++ i)
    g[0][i] = s[i];
    for(int i =  7, j = 0;j < 4;i --, ++ j)
        g[1][j] = s[i];
}

string get(){
    string res;
    for(int i = 0;i < 4; ++ i)
    res += g[0][i];
    for(int i = 3;i >= 0;i --)
        res += g[1][i];
    return res;
}

string move1(string s){
    set(s);
    for(int i = 0;i < 4; ++i)
    swap(g[1][i], g[0][i]);
    return get();
}

string move2(string s){
    set(s);
    char a = g[0][3], b = g[1][3];
    for(int i = 3; i >= 0;i -- ){
        g[1][i] = g[1][i - 1];
        g[0][i] = g[0][i - 1];
    }
    g[0][0] = a, g[1][0] = b;
    return get();
}

string move3(string s){
    set(s);
    char v = g[0][1];
    g[0][1] = g[1][1];
    g[1][1] = g[1][2];
    g[1][2] = g[0][2];
    g[0][2] = v;
    return get();
}

int bfs(string s, string e){
    if(s == e)return 0;

    queue<string>q;
    q.push(s);
    dist[s] = 0;

    while(q.size()){
        string t = q.front();
        q.pop();

        string m[3];
        m[0] = move1(t);
        m[1] = move2(t);
        m[2] = move3(t);

        for(int i = 0;i <3;i ++){
            if(!dist.count(m[i])){
                dist[m[i]] = dist[t] + 1;
                pre[m[i]] = {'A' + i, t};
                q.push(m[i]);
                if(m[i] == ed)return dist[ed];
            }
        }
    }
    return -1;
}

int main(){
    int x;
     for(int i = 0;i < 8;++i){
        cin >> x;
        ed += char(x + '0');
     }
     for(int i = 1;i <= 8; ++i)
     start += char('0' + i);

     int step = bfs(start, ed);

     cout << step << endl;
     string res;
     while(ed != start){
         res += pre[ed].first;
         ed = pre[ed].second;
     }
     reverse(res.begin(),res.end());
     if(step > 0)cout << res << endl;
     return 0;
}

C、AcWing 175. 电路维修(双端队列广搜)

在这里插入图片描述

01权值图可以使用双端队列广搜:≈ 简化版dijkstra
0:放到队头 = > => =>满足两段性和单调性

两段性和单调性:

 x x x x x x + 1 x + 1

1:放到队尾
x = > => => 0 = > => => 队头
x + 1 = > => => 1 = > => => 队尾

但是每个点都可能入队多次

然后就和一般的bfs一摸一样

本题中可以直接走边权为0,需要旋转边权为1

求最短路
也可以用dijkstra = > => => 只要是非负权值

当起点与终点的奇偶行不一样就无法到达
奇偶性不一样就永远都无法到达
奇点:横纵坐标和为奇数的点
偶点:横纵坐标和为偶数的点
因为每次都一条边就是横纵坐标都+1总共+2,因此奇点只能走到奇点,偶点只能走到偶点
起点为0偶点,所以如果终点为奇点就无法到达
id数组的作用是用来将格点坐标转换成线的坐标,线的坐标是用来判断这一步能不能走。

1     2
   0 
4     3

在这里插入图片描述

注意

char cs[] = "\\/\\/";

\ 是关键字符,要使用的时候应该写成\\

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

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 510, M = N * N;

int n, m;
char g[N][N];
int dist[N][N];
bool st[N][N];

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

    char cs[] = "\\/\\/";
    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())
    {
        PII t = q.front();
        q.pop_front();

        if (st[t.x][t.y]) continue;
        st[t.x][t.y] = true;

        for (int i = 0; i < 4; i ++ )
        {
            int a = t.x + dx[i], b = t.y + dy[i];
            if (a < 0 || a > n || b < 0 || b > m) continue;

            int ca = t.x + ix[i], cb = t.y + iy[i];
            int d = dist[t.x][t.y] + (g[ca][cb] != cs[i]);

            if (d < dist[a][b])
            {
                dist[a][b] = d;

                if (g[ca][cb] != cs[i]) q.push_back({a, b});
                else q.push_front({a, b});
            }
        }
    }

    return dist[n][m];
}

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]);

        int t = bfs();

        if (t == 0x3f3f3f3f) puts("NO SOLUTION");
        else printf("%d\n", bfs());
    }

    return 0;
}
©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页