BFS__FloodFill、最短路、多源BFS、最小步数、双端队列广搜

本文介绍了FloodFill洪水覆盖算法在解决池塘计数、城堡问题以及山峰和山谷问题中的应用,同时讨论了最短路模型在迷宫问题、电路维修和多源BFS问题中的重要性,最后提到了双端队列广搜在电路维修问题中的策略。这些问题展示了BFS等算法在处理图论和路径搜索问题时的有效性。
摘要由CSDN通过智能技术生成

目录

FloodFill 洪水覆盖算法

1097. 池塘计数

1098. 城堡问题

1106. 山峰和山谷

最短路模型

1076. 迷宫问题

188. 武士风度的牛

1100. 抓住那头牛

多源BFS

173. 矩阵距离

最小步数模型

845. 八数码

1107. 魔板

双端队列广搜

175. 电路维修


FloodFill 洪水覆盖算法

可以在线性时间复杂度内,找到某个点的连通块(使用bfs实现不会有爆栈风险)

1097. 池塘计数

农夫约翰有一片 N∗M 的矩形土地。
最近,由于降雨的原因,部分土地被水淹没了。
现在用一个字符矩阵来表示他的土地。
每个单元格内,如果包含雨水,则用”W”表示,如果不含雨水,则用”.”表示。
现在,约翰想知道他的土地中形成了多少片池塘。
每组相连的积水单元格集合可以看作是一片池塘。
每个单元格视为与其上、下、左、右、左上、右上、左下、右下八个邻近单元格相连。
请你输出共有多少片池塘,即矩阵中共有多少片相连的”W”块。

输入格式
第一行包含两个整数 N 和 M。
接下来 N 行,每行包含 M 个字符,字符为”W”或”.”,用以表示矩形土地的积水状况,字符之间没有空格。
输出格式
输出一个整数,表示池塘数目。

输入样例:
10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.
输出样例:
3
#include <bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

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

int n, m;
PII q[M];           //M = N * N
char g[N][N];
bool st[N][N];

void bfs(int sx, int sy)
{
    int hh = 0, tt = 0;
    q[0] = {sx, sy};
    st[sx][sy] = true;
    
    while (hh <= tt)
    {
        PII t = q[hh ++];
        
        for (int i = t.x - 1; i <= t.x + 1; i ++)       //遍历当前点的八个方向
            for (int j = t.y - 1; j <= t.y + 1; j ++)
            {
                if (i == t.x && j == t.y) continue;     //为当前点跳过
                if (i < 0 || i >= n || j < 0 || j >= m) continue;   //超出边界跳过
                if (g[i][j] == '.' || st[i][j]) continue;       //无水或已遍历跳过
                
                q[++ tt] = {i, j};      //将该新点加入队列
                st[i][j] = true;        //标记为已遍历
            }
    }
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i ++) scanf("%s", g[i]);
    
    int cnt = 0;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < m; j ++)
        {
            if (! st[i][j] && g[i][j] == 'W')       //还未遍历且含雨水
            {
                bfs(i, j);
                cnt ++;         //每遍历一个连通块就++一次
            }
        }
    
    cout << cnt;
    
    return 0;
}

1098. 城堡问题

1 2 3 4 5 6 7
#############################
1 # | # | # | | #
#####---#####---#---#####---#
2 # # | # # # # #
#---#####---#####---#####---#
3 # | | # # # # #
#---#########---#####---#---#
4 # # | | | | # #
#############################
(图 1)

# = Wall
| = No wall
- = No wall

方向:上北下南左西右东。
图1是一个城堡的地形图。
请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。
城堡被分割成 m∗n个方格区域,每个方格区域可以有0~4面墙。
注意:墙体厚度忽略不计。

输入格式
第一行包含两个整数 m 和 n,分别表示城堡南北方向的长度和东西方向的长度。
接下来 m 行,每行包含 n 个整数,每个整数都表示平面图对应位置的方块的墙的特征。
每个方块中墙的特征由数字 P 来描述,我们用1表示西墙,2表示北墙,4表示东墙,8表示南墙,P 为该方块包含墙的数字之和。
例如,如果一个方块的 P 为3,则 3 = 1 + 2,该方块包含西墙和北墙。
城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。
输入的数据保证城堡至少有两个房间。
输出格式
共两行,第一行输出房间总数,第二行输出最大房间的面积(方块数)。
数据范围
1≤m,n≤50
0≤P≤15

输入样例:
4 7
11 6 11 6 3 10 6
7 9 6 13 5 15 5
1 10 12 7 13 7 5
13 11 10 8 10 12 13
输出样例:
5
9
#include <bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

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

int dx[4] = {0, -1, 0, 1}, dy[4] = {-1, 0, 1, 0};   //西北东南四个方向

int n, m;
PII q[M];
int g[N][N];
bool st[N][N];

int bfs(int sx, int sy)
{
    int hh = 0, tt = 0;
    int area = 0;
    
    q[0] = {sx, sy};
    st[sx][sy] = true;
    
    while (hh <= tt)
    {
        PII t = q[hh ++];
        area ++;                                    //记录每一个连通块里的数量
        
        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;
            if (st[a][b]) continue;
            if (g[t.x][t.y] >> i & 1) continue;     //当前要走方向有墙
            
            q[++ tt] = {a, b};
            st[a][b] = true;
        }
    }
    return area;
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < m; j ++) 
            scanf("%d", &g[i][j]);
            
    int cnt = 0, area = 0;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < m; j ++)
        {
            if (! st[i][j])
            {
                area = max(area, bfs(i, j));        //记录最大值
                cnt ++;                             //连通块个数
            }
        }
    
    cout << cnt << endl << area;
    
    return 0;
}

1106. 山峰和山谷

FGD小朋友特别喜欢爬山,在爬山的时候他就在研究山峰和山谷。
为了能够对旅程有一个安排,他想知道山峰和山谷的数量。
给定一个地图,为FGD想要旅行的区域,地图被分为 n×n 的网格,每个格子 (i,j) 的高度 w(i,j) 是给定的。
若两个格子有公共顶点,那么它们就是相邻的格子,如与 (i,j) 相邻的格子有 (i−1,j−1),(i−1,j),(i−1,j+1),(i,j−1),(i,j+1),(i+1,j−1),(i+1,j),(i+1,j+1)
我们定义一个格子的集合 S 为山峰(山谷)当且仅当:
S 的所有格子都有相同的高度。
S 的所有格子都连通。
对于 s 属于 S,与 s 相邻的 s′ 不属于 S,都有 ws>ws′(山峰),或者 ws<ws′(山谷)。
如果周围不存在相邻区域,则同时将其视为山峰和山谷。
你的任务是,对于给定的地图,求出山峰和山谷的数量,如果所有格子都有相同的高度,那么整个地图即是山峰,又是山谷。
输入格式
第一行包含一个正整数 n,表示地图的大小。
接下来一个 n×n 的矩阵,表示地图上每个格子的高度 w。
输出格式
共一行,包含两个整数,表示山峰和山谷的数量。
数据范围
1≤n≤1000
0≤w≤10
输入样例1:
5
8 8 8 7 7
7 7 8 8 7
7 7 7 7 7
7 8 8 7 8
7 8 8 8 8
输出样例1:
2 1
输入样例2:
5
5 7 8 3 1
5 5 7 6 6
6 6 6 2 8
5 7 2 5 8
7 1 0 1 7
输出样例2:
3 3

样例解释

样例1:

样例2:

#include <bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

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

int n;
PII q[M];
int h[N][N];
bool st[N][N];

void bfs(int sx, int sy, bool &hash, bool &hasl)
{
    int hh = 0, tt = 0;
    q[0] = {sx, sy};
    st[sx][sy] = true;
    
    while (hh <= tt)
    {
        PII t = q[hh ++];
        
        for (int i = t.x - 1; i <= t.x + 1; i ++)
            for (int j = t.y - 1; j <= t.y + 1; j ++)
            {
                if (i == t.x && j == t.y) continue;
                if (i < 0 || i >= n || j < 0 || j >= n) continue;
                if (h[i][j] != h[t.x][t.y])             //高度不一样则更新
                {
                    if (h[i][j] > h[t.x][t.y]) hash = true;     //存在比当前点高的点
                    else hasl = true;                           //存在比当前点低的点
                }
                else if (! st[i][j])
                {
                    q[++ tt] = {i, j};
                    st[i][j] = true;
                }
            }
    }
} 

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n; j ++)
            scanf("%d", &h[i][j]);
            
    int peak = 0, valley = 0;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n; j ++)
        {
            if (! st[i][j])
            {
                bool hash = false, hasl = false;
                bfs(i, j, hash, hasl);
                if (! hash) peak ++;        //不存在比当前点高的点则山峰++
                if (! hasl) valley ++;      //不存在比当前点低的点则山谷++
            }
        }
        
    printf("%d %d\n", peak, valley);
    
    return 0;
}

最短路模型

1076. 迷宫问题

给定一个 n×n 的二维数组,如下所示:

int maze[5][5] = {

0, 1, 0, 0, 0,

0, 1, 0, 1, 0,

0, 0, 0, 0, 0,

0, 1, 1, 1, 0,

0, 0, 0, 1, 0,

};
它表示一个迷宫, 其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
数据保证至少存在一条从左上角走到右下角的路径。

输入格式
第一行包含整数 n。
接下来 n 行,每行包含 n 个整数 0 或 1,表示迷宫。
输出格式
输出从左上角到右下角的最短路线,如果答案不唯一,输出任意一条路径均可。
按顺序,每行输出一个路径中经过的单元格的坐标,左上角坐标为 (0,0),右下角坐标为 (n−1,n−1)。

数据范围
0≤n≤1000
输入样例:
5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
输出样例:
0 0
1 0
2 0
2 1
2 2
2 3
2 4
3 4
4 4
#include<bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

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

int n;
int g[N][N];
PII q[M];
PII pre[N][N];          //记录当前点是由哪一个点转移的
int dx[4] = {0, -1 , 0, 1}, dy[4] = {-1, 0, 1, 0};

void bfs(int sx, int sy)
{
    int hh = 0, tt = 0;
    q[0] = {sx, sy};
    memset(pre, -1, sizeof pre);
    //pre[sx][sy] = {0, 0};
    
    while (hh <= tt)
    {
        PII t = q[hh ++];
        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 >= n) continue;
            if (pre[a][b].x != -1) continue;
            if (g[a][b]) continue;
            
            q[++ tt] = {a, b};
            pre[a][b] = t;
        }
    }
    
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n; j ++)
            scanf("%d", &g[i][j]);
            
    bfs(n - 1, n - 1);          //反向遍历,则{0, 0}为终点
    
    PII en = {0, 0};
    while (true)                //从{0, 0}开始输出每一个前驱点,即为所求路径
    {
        printf("%d %d\n", en.x, en.y);
        if (en.x == n - 1 && en.y == n - 1) break;
        en = pre[en.x][en.y];
    }
    
    return 0;
}

188. 武士风度的牛

农民 John 有很多牛,他想交易其中一头被 Don 称为 The Knight 的牛。
这头牛有一个独一无二的超能力,在农场里像 Knight 一样地跳(就是我们熟悉的象棋中马的走法)。
虽然这头神奇的牛不能跳到树上和石头上,但是它可以在牧场上随意跳,我们把牧场用一个 x,y的坐标图来表示。
这头神奇的牛像其它牛一样喜欢吃草,给你一张地图,上面标注了 The Knight 的开始位置,树、灌木、石头以及其它障碍的位置,除此之外还有一捆草。
现在你的任务是,确定 The Knight 要想吃到草,至少需要跳多少次。
The Knight 的位置用 K 来标记,障碍的位置用 * 来标记,草的位置用 H 来标记。
这里有一个地图的例子:

11 | . . . . . . . . . .
10 | . . . . * . . . . .
9 | . . . . . . . . . .
8 | . . . * . * . . . .
7 | . . . . . . . * . .
6 | . . * . . * . . . H
5 | * . . . . . . . . .
4 | . . . * . . . * . .
3 | . K . . . . . . . .
2 | . . . * . . . . . *
1 | . . * . . . . * . .
0 ----------------------
1
0 1 2 3 4 5 6 7 8 9 0
The Knight 可以按照下图中的 A,B,C,D…这条路径用 5 次跳到草的地方(有可能其它路线的长度也是 5):

11 | . . . . . . . . . .
10 | . . . . * . . . . .
9 | . . . . . . . . . .
8 | . . . * . * . . . .
7 | . . . . . . . * . .
6 | . . * . . * . . . F<
5 | * . B . . . . . . .
4 | . . . * C . . * E .
3 | .>A . . . . D . . .
2 | . . . * . . . . . *
1 | . . * . . . . * . .
0 ----------------------
1
0 1 2 3 4 5 6 7 8 9 0
注意: 数据保证一定有解。
输入格式
第 1 行: 两个数,表示农场的列数 C 和行数 R。
第 2..R+1 行: 每行一个由 C 个字符组成的字符串,共同描绘出牧场地图。
输出格式
一个整数,表示跳跃的最小次数。
数据范围
1≤R,C≤150
输入样例:
10 11
..........
....*.....
..........
...*.*....
.......*..
..*..*...H
*.........
...*...*..
.K........
...*.....*
..*....*..
输出样例:
5
#include <bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

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

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

int dx[] = {2, 2, -1, -1, 1, 1, -2, -2};
int dy[] = {1, -1, 2, -2, 2, -2, 1, -1};

int bfs()
{
    int sx, sy;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < m; j ++)
            if (g[i][j] == 'K')
                sx = i, sy = j;
    
    int hh = 0, tt = 0;
    q[0] = {sx, sy};
    
    memset(dist, -1, sizeof dist);
    dist[sx][sy] = 0;
    
    while (hh <= tt)
    {
        PII t = q[hh ++];
        
        for (int i = 0; i < 8; i ++)
        {
            int a = t.x + dx[i], b = t.y + dy[i];
            if (a < 0 || a >= n || b < 0 || b >= m) continue; 
            if (g[a][b] == '*') continue;
            if (dist[a][b] != -1) continue;
            if (g[a][b] == 'H') return dist[t.x][t.y] + 1;
            
            q[++ tt] = {a, b};
            dist[a][b] = dist[t.x][t.y] + 1;
        }
    }
}


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

1100. 抓住那头牛

农夫知道一头牛的位置,想要抓住它。
农夫和牛都位于数轴上,农夫起始位于点 N,牛位于点 K。
农夫有两种移动方式:
从 X 移动到 X−1 或 X+1,每次移动花费一分钟
从 X 移动到 2∗X,每次移动花费一分钟
假设牛没有意识到农夫的行动,站在原地不动。
农夫最少要花多少时间才能抓住牛?

输入格式
共一行,包含两个整数N和K。
输出格式
输出一个整数,表示抓到牛所花费的最少时间。
数据范围
0≤N,K≤10^5
输入样例:
5 17
输出样例:
4
#include <bits/stdc++.h>

using namespace std;

const int N = 100010;

int n, k;
int q[N];
int dist[N];

int bfs()
{
    int hh = 0, tt = 0;
    q[0] = n;
    memset(dist, -1, sizeof dist);
    dist[n] = 0;
    
    while (hh <= tt)
    {
        int t = q[hh ++];
        if (t == k) return dist[t];
        
        if (t + 1 < N && dist[t + 1] == -1)
        {
            dist[t + 1] = dist[t] + 1;
            q[++ tt] = t + 1;
        }
        if (t - 1 >= 0 && dist[t - 1] == -1)
        {
            dist[t - 1] = dist[t] + 1;
            q[++ tt] = t - 1;
        }
        if (t * 2 < N && dist[t * 2] == -1)
        {
            dist[t * 2] = dist[t] + 1;
            q[++ tt] = t * 2;
        }
    }
    return -1;
}

int main()
{
    cin >> n >> k;
    
    cout << bfs();
    
    return 0;
}

多源BFS

173. 矩阵距离

给定一个 N 行 M 列的 01 矩阵 A,A[i][j] 与A[k][l] 之间的曼哈顿距离定义为:
dist(A[i][j],A[k][l])=|i−k|+|j−l|
输出一个 N 行 M 列的整数矩阵 B,其中:
B[i][j](min1≤x≤N,1≤y≤M, A[x][y]=1) dist(A[i][j],A[x][y])

输入格式
第一行两个整数 N,M。
接下来一个N 行M 列的01 矩阵, 数字之间没有空格。
输出格式
一个 N 行 M 列的矩阵 B,相邻两个整数之间用一个空格隔开。
数据范围
1≤N,M≤1000
输入样例:
3 4
0001
0011
0110
输出样例:
3 2 1 0
2 1 0 0
1 0 0 1

思路:题目要求01矩阵终点每个数0距离数1的距离最小,而有多个数1,所以为多起点问题,只需将所有数1都加入初始队列中。

#include <bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 1010;

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

int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};

void bfs()
{
    memset(dist, 0x3f, sizeof dist);
    queue<PII> q;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < m; j ++)
        {
            if (g[i][j] == '1')
            {
                dist[i][j] = 0;
                q.push({i, j});
            }
        }
    
    while (q.size())
    {
        PII t = q.front();
        q.pop();
        
        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;
            
            if (dist[a][b] > dist[t.x][t.y] + 1)
            {
                dist[a][b] = dist[t.x][t.y] + 1;
                q.push({a, b});
            }
        }
    }
}

int main()
{
    cin >> 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;
}

最小步数模型

845. 八数码

在一个 3×3 的网格中,1∼8 这 8 个数字和一个 x 恰好不重不漏地分布在这 3×3 的网格中。
例如:
1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x 与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
1 2 3
4 5 6
7 8 x
例如,示例中图形就可以通过让 x 先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3 1 2 3 1 2 3 1 2 3
x 4 6 4 x 6 4 5 6 4 5 6
7 5 8 7 5 8 7 x 8 7 8 x

现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。
输入格式
输入占一行,将 3×3 的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
输出格式
输出占一行,包含一个整数,表示最少交换次数。
如果不存在解决方案,则输出 −1。

输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19

算法思路

用一个队列保存当前获得的排列(字符串)

用一个哈希表保存各个排列(字符串)与从开始排列到该排列的交换次数。

从队列中取出队头这个排列,计算出这个排列通过交换能得到的所有排列。如果得到的排列是新排列(哈希表中没有这个排列),就把这个新排列放入队尾,哈希表中记录新排列对应的交换次数。

如果在上述过程中得到了结果排列,则输出交换次数,结束。(即是交换次数最小)

如果最终没有得到结果排列。输出-1。

#include <bits/stdc++.h>
 
using namespace std;
 
string start, ed = "12345678x";         //ed为正确排列
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
 
int bfs()
{
    queue<string> q;
    q.push(start);
    unordered_map<string, int> d;       //每一个排列对应的交换次数
    d[start] = 0;
    
    while (q.size())
    {
        string t = q.front();
        q.pop();
        
        int dist = d[t];
        if (t == ed) return dist;
        
        int k = t.find('x');
        int x = k / 3, y = k % 3;           //得到x当前的横纵坐标
        for (int i = 0; i < 4; i ++)        //遍历x的上右下左四个方向
        {
            int a = x + dx[i], b = y + dy[i];
            if (a < 0 || a >= 3 || b < 0 || b >= 3) continue;
            swap(t[k], t[a * 3 + b]);       //将x与该合法位置交换
            if (!d[t])                      //得到新的排列
            {
                d[t] = dist + 1;            //更新该新排列的次数
                q.push(t);                  //将该新排列加入队列
            }
            swap(t[k], t[a * 3 + b]);       //恢复当前排列
        }
    }
    return -1;          //遍历了所有排列还未返回,则没有解
}
 
int main()
{
    for (int i = 0; i < 9; i ++)        //将3x3的排列转化为字符串
    {
        char c;
        cin >> c;
        start += c; 
    }
    
    cout << bfs();
    
    return 0;
}

1107. 魔板

Rubik 先生在发明了风靡全球的魔方之后,又发明了它的二维版本——魔板。
这是一张有 8 个大小相同的格子的魔板:
1 2 3 4
8 7 6 5
我们知道魔板的每一个方格都有一种颜色。
这 8 种颜色用前 8 个正整数来表示。
可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。
对于上图的魔板状态,我们用序列 (1,2,3,4,5,6,7,8) 来表示,这是基本状态。

这里提供三种基本操作,分别用大写字母 A,B,C 来表示(可以通过这些操作改变魔板的状态):
A:交换上下两行;
B:将最右边的一列插入到最左边;
C:魔板中央对的4个数作顺时针旋转。

下面是对基本状态进行操作的示范:
A:
8 7 6 5
1 2 3 4
B:
4 1 2 3
5 8 7 6
C:
1 7 2 4
8 6 3 5
对于每种可能的状态,这三种基本操作都可以使用。
你要编程计算用最少的基本操作完成基本状态到特殊状态的转换,输出基本操作序列。
注意:数据保证一定有解。

输入格式
输入仅一行,包括 8 个整数,用空格分开,表示目标状态。
输出格式
输出文件的第一行包括一个整数,表示最短操作序列的长度。
如果操作序列的长度大于0,则在第二行输出字典序最小的操作序列。
数据范围
输入数据中的所有数字均为 1 到 8 之间的整数。
输入样例:
2 6 8 4 5 7 3 1
输出样例:
7
BCABCCB

注:unordered_map和map的区别

1、实现不同

unordered_map底层是用哈希表实现的

map底层是用红黑树实现的

2、性能不同

unordered_map是不按键值排序的,插入的时间是O(logn),查询时间是O(1)

map是按键值排序的,插入的时间是O(logn),查询时间是O(logn)

3、使用范围不同

unordered_map的使用比较局限,它的key只能是int、double等基本类型以及string,而不能是自己定义的结构体

map可以支持所有类型的键值对

#include <bits/stdc++.h>

using namespace std;

unordered_map<string, int> dist;
unordered_map<string, pair<char, string>> pre;

string get(string t, int op)
{
    string k;
    if (op == 0) k = {t[4], t[5], t[6], t[7], t[0], t[1], t[2], t[3]};
    else if (op == 1) k = {t[3], t[0], t[1], t[2], t[7], t[4], t[5], t[6]};
    else k = {t[0], t[5], t[1], t[3], t[4], t[6], t[2], t[7]};
    return k;
}

int bfs(string start, string end)
{
    queue<string> q;
    q.push(start);
    dist[start] = 0;
    
    while (q.size())
    {
        string t = q.front();
        q.pop();
        
        if (t == end) return dist[t];
        
        for (int i = 0; i < 3; i ++)
        {
            string g = get(t, i);
            if (! dist.count(g))
            {
                dist[g] = dist[t] + 1;
                pre[g] = {'A' + i, t};
                q.push(g);
            }
        }
    }
}

int main()
{
    int a[8];
    for (int i = 0; i < 8; i ++) cin >> a[i];
    string start, end;
    start = "12348765";
    for (int i = 0; i < 4; i ++) end += a[i] + '0';
    for (int i = 7; i >= 4; i --) end += a[i] + '0';
    
    cout << bfs(start, end) << endl;
    
    string res;
    while (start != end)
    {
        res += pre[end].first;
        end = pre[end].second;
    }
    
    reverse(res.begin(), res.end());
    cout << res;
    
    return 0;
}

双端队列广搜

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

操作:

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

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

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

175. 电路维修

达达是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女翰翰,从而被收留在地球上。
翰翰的家里有一辆飞行车。
有一天飞行车的电路板突然出现了故障,导致无法启动。
电路板的整体结构是一个 R 行 C 列的网格(R,C≤500),如下图所示。

每个格点都是电线的接点,每个格子都包含一个电子元件。
电子元件的主要部分是一个可旋转的、连接一条对角线上的两个接点的短电缆。
在旋转之后,它就可以连接另一条对角线的两个接点。
电路板左上角的接点接入直流电源,右下角的接点接入飞行车的发动装置。
达达发现因为某些元件的方向不小心发生了改变,电路板可能处于断路的状态。
她准备通过计算,旋转最少数量的元件,使电源与发动装置通过若干条短缆相连。
不过,电路的规模实在是太大了,达达并不擅长编程,希望你能够帮她解决这个问题。
注意:只能走斜向的线段,水平和竖直线段不能走。

输入格式
输入文件包含多组测试数据。
第一行包含一个整数 T,表示测试数据的数目。
对于每组测试数据,第一行包含正整数 R 和 C,表示电路板的行数和列数。
之后 R 行,每行 C 个字符,字符是"/"和"\"中的一个,表示标准件的方向。
输出格式
对于每组测试数据,在单独的一行输出一个正整数,表示所需的最小旋转次数。
如果无论怎样都不能使得电源和发动机之间连通,输出 NO SOLUTION。
数据范围
1≤R,C≤500
1≤T≤5
输入样例:
1
3 5
\\/\\
\\///
/\\\\
输出样例:
1
样例解释
样例的输入对应于题目描述中的情况。
只需要按照下面的方式旋转标准件,就可以使得电源和发动机之间连通。

思路:因为只走斜线,则横纵坐标都会同时变化1, 所有若横纵坐标奇偶性不同(即相加为奇数)无解

从当前点到要去的点走的路径与本身路径相同,则走该步不需要旋转则花费为0,更新后加入队头

反之则需要旋转花费为1,更新后加入队尾

注意:坐标点变化与路径之间的对应关系

#include <bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 510;

int n, m;
char g[N][N];
int dist[N][N];
bool st[N][N];
char cs[5] = "\\//\\";
int dx[4] = {-1, -1, 1, 1}, dy[4] = {-1, 1, -1, 1};
int ix[4] = {-1, -1, 0, 0}, iy[4] = {-1, 0, -1, 0};

int bfs()
{
    deque<PII> q;            //双端队列
    q.push_back({0, 0});
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);
    dist[0][0] = 0;
    
    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] + (cs[i] != g[ca][cb]);
            if (d < dist[a][b])
            {
                dist[a][b] = d;
                if (cs[i] == g[ca][cb]) q.push_front({a, b});
                else q.push_back({a, b});
            }
        }
    }
    return dist[n][m];
}

int main()
{
    int t;
    cin >> t;
    while (t --)
    {
        cin >> n >> m;
        for (int i = 0; i < n; i ++) scanf("%s", g[i]);
        
        if (n + m & 1) cout << "NO SOLUTION" << endl;    //n+m为奇数则不可能走到
        else cout << bfs() << endl;
    }
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值