目录
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;
}