有参加蓝桥杯的同学可以给博主点个关注,博主也在准备蓝桥杯,可以跟着博主的博客一起刷题。
蓝桥杯
BFS
栈 → DFS
队列 → BFS
深搜dfs,一搜到底;宽搜bfs,一层一层的遍历。
queue: []
每次取出队首元素,将拓展出的所有元素放到队尾。
下图就是队列取出元素及放入元素:
我们可以发现,入队的顺序就是层序遍历的顺序。
宽搜模板
① 判重数组 st[]
入队时判重
② queue
伪代码模板
queue ← 初始状态
while (queue非空) {
t ← 队首
for (拓展t) {
ver ← 新节点
if (!st[ver]) { // 判重
ver → 队尾
}
}
}
例题
AcWing 1101. 献给阿尔吉侬的花束
模板题
第一个样例:
需要走5步,答案应该是5。
第三个样例:
被墙挡住了,无法吃到奶酪,输出“oop!”。
我们用bfs遍历:
bfs可以找到一条合法路径,且步数最小,它会把同等距离的点全部搜一遍。
当我们第一次扩展到终点的时候,必然就是最短距离;如果搜不到,就是无解。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
public class Main {
static final int N = 210;
static int r, c;
static PII start, end;
static char[][] g = new char[N][N]; // 存地图
static int[][] dist = new int[N][N]; // 存距离
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(in.readLine().trim());
while (T-- > 0) {
String[] s = in.readLine().split(" ");
r = Integer.parseInt(s[0]);
c = Integer.parseInt(s[1]);
for (int i = 0; i < r; i++) {
char[] arr = in.readLine().toCharArray();
for (int j = 0; j < c; j++) {
g[i][j] = arr[j];
if (g[i][j] == 'S') start = new PII(i, j);
else if (g[i][j] == 'E') end = new PII(i, j);
}
}
int distance = bfs(start, end);
if (distance == -1) System.out.println("oop!");
else System.out.println(distance);
}
}
private static int bfs(PII start, PII end) {
Queue<PII> q = new LinkedList<>();
for(int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
dist[i][j] = -1; // 将dist值初始化为-1
}
}
dist[start.x][start.y] = 0;
q.offer(start);
int[] dx = new int[]{-1, 0, 1, 0}, dy = new int[]{0, 1, 0, -1}; // 定义坐标偏移量
while (q.size() > 0) {
PII t = q.poll();
for (int i = 0; i < 4; i++) {
int x = t.x + dx[i], y = t.y + dy[i];
if (x < 0 || x >= r || y < 0 || y >= c) continue; // 出界
if (g[x][y] == '#') continue; // 墙
if (dist[x][y] != -1) continue; // 之前已经遍历过
dist[x][y] = dist[t.x][t.y] + 1; // 更新距离 +1
if (end.x == x && end.y == y) return dist[x][y];
q.offer(new PII(x, y));
}
}
return -1;
}
static class PII {
int x;
int y;
public PII(int x, int y) {
this.x = x;
this.y = y;
}
}
}
AcWing 1113. 红与黑
Flood Fill
洪水灌溉算法,本题用bfs
。
有多少个'.'和'@'相连
从起点出发,向四周进行广度优先遍历搜索;一圈一圈来扩,每一次取出来队首没有被扩过的点。
套用bfs的模板即可。
import java.util.Scanner;
import java.util.LinkedList;
import java.util.Queue;
public class Main {
static final int N = 25;
static int n, m;
static char[][] g = new char[N][N];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
m = sc.nextInt();
n = sc.nextInt();
if (n == 0 && m == 0) break;
int x = 0, y = 0; // 代表自己的位置
for (int i = 0; i < n; i++) {
char[] arr = sc.next().toCharArray();
for (int j = 0; j < m; j++) {
g[i][j] = arr[j];
if (g[i][j] == '@') {
x = i;
y = j;
}
}
}
System.out.println(bfs(x, y));
}
}
private static int bfs(int sx, int sy) {
Queue<PII> q = new LinkedList<>();
q.offer(new PII(sx, sy));
g[sx][sy] = '#'; // 已经走过 标记为红色 不能再走了
int res = 0;
int[] dx = new int[]{-1, 0, 1, 0}, dy = new int[]{0, 1, 0, -1};
while (!q.isEmpty()) {
PII t = q.poll();
res++;
for (int i = 0; i < 4; i++) {
int x = t.x + dx[i], y = t.y + dy[i];
if (x < 0 || x >= n || y < 0 || y >= m) continue; // 越界
if (g[x][y] != '.') continue; // 已经被扩展 或者就是红色砖
g[x][y] = '#';
q.offer(new PII(x, y));
}
}
return res;
}
static class PII {
int x;
int y;
public PII(int x, int y) {
this.x = x;
this.y = y;
}
}
}
AcWing 1096. 地牢大师
例题第一题的扩展:三维bfs
求的是最短时间,每一次移动消耗的时间是一样的,所以可以用第一题的做法,直接宽搜就可以了:从起点开始宽搜,每次向上下左右没有扩展到的格子扩展一层,当我们第一次找到终点的时候,就是我们的最短距离。
二维空间我们可以用4个偏移量来表示,那么三维就可以用6个偏移量来表示:
import java.util.Scanner;
import java.util.ArrayList;
import java.util.Queue;
import java.util.LinkedList;
public class Main {
static final int N = 110;
static int L, R, C;
static Point start, end;
static char[][][] g = new char[N][N][N]; // 存地图
static int[][][] dist = new int[N][N][N]; // 存距离
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
L = sc.nextInt();
R = sc.nextInt();
C = sc.nextInt();
if (L == 0 && R == 0 && C == 0) break;
for (int i = 0; i < L; i++) {
for (int j = 0; j < R; j++) {
char[] data = sc.next().toCharArray();
for (int k = 0; k < C; k++) {
g[i][j][k] = data[k];
if (g[i][j][k] == 'S') start = new Point(i, j, k);
else if (g[i][j][k] == 'E') end = new Point(i, j, k);
}
}
}
int distance = bfs(start, end);
if (distance == -1) System.out.println("Trapped!");
else System.out.println("Escaped in " + distance + " minute(s).");
}
}
private static int bfs(Point start, Point end) {
Queue<Point> q = new LinkedList<>();
q.offer(start);
for (int i = 0; i < L; i++) {
for (int j = 0; j < R; j++) {
for (int k = 0; k < C; k++) {
dist[i][j][k] = -1;
}
}
}
dist[start.x][start.y][start.z] = 0;
int[] dx = new int[]{1, -1, 0, 0, 0, 0};
int[] dy = new int[]{0, 0, 1, -1, 0, 0};
int[] dz = new int[]{0, 0, 0, 0, 1, -1};
while (!q.isEmpty()) {
Point t = q.poll();
for (int i = 0; i < 6; i++) {
int x = t.x + dx[i], y = t.y + dy[i], z = t.z + dz[i];
if (x < 0 || x >= L || y < 0 || y >= R || z < 0 || z >= C) continue; // 出界
if (g[x][y][z] == '#') continue; // 有障碍物
if (dist[x][y][z] != -1) continue; // 之前走到过
dist[x][y][z] = dist[t.x][t.y][t.z] + 1;
if (x == end.x && y == end.y && z == end.z) return dist[x][y][z];
q.offer(new Point(x, y, z));
}
}
return -1;
}
static class Point {
int x;
int y;
int z;
public Point(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}
}
第九届2018年蓝桥杯真题
AcWing 1233. 全球变暖
JavaB组第9题
例题第二题的扩展,Flood Fill
算法。
第一个样例,被蓝色划掉的陆地会被淹没掉:
求会被完全淹没的岛屿数量,红色会被完全淹没,绿色还会剩一个陆地。
宽搜:从起点出发,向四周bfs,每一次扩展和这个点相邻的所有点;只要给我们一个点,我们就可以一层一层的把一个点的连通块全部搜到。
① 有多少个岛屿(连通块)=> 判重数组st[][]
伪代码
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (!st[i][j] && g[i][j] == '#') {
if (bfs(i, j)) cnt++;
}
}
}
② 有多少个岛屿(连通块)会被完全淹没掉
利用bfs
计算当前岛屿连通块的数量total
,以及这些连通块中与海相连(处于边界)的数量bound
,若total == bound
,则表示这个岛屿会被完全淹没。
import java.util.Scanner;
import java.util.Queue;
import java.util.LinkedList;
public class Main {
static final int N = 1010;
static int n;
static char[][] g = new char[N][N];
static boolean[][] st = new boolean[N][N];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int i = 0; i < n; i++) g[i] = sc.next().toCharArray();
int cnt = 0; // 被淹没岛屿数
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (!st[i][j] && g[i][j] == '#') {
if (bfs(i, j)) cnt++;
}
}
}
System.out.print(cnt);
}
private static boolean bfs(int sx, int sy) {
Queue<PII> q = new LinkedList<>();
q.offer(new PII(sx, sy));
st[sx][sy] = true;
int[] dx = new int[]{-1, 0, 1, 0}, dy = new int[]{0, 1, 0, -1};
int total = 0, bound = 0; // 连通块数和边界数
while (!q.isEmpty()) {
PII t = q.poll();
total++;
boolean is_bound = false;
for (int i = 0; i < 4; i++) {
int x = t.x + dx[i], y = t.y + dy[i];
if (x < 0 || x >= n || y < 0 || y >= n) continue; // 出界
if (st[x][y]) continue; // 说明已遍历过
if (g[x][y] == '.') { // 如果周围有海
is_bound = true;
continue;
}
q.offer(new PII(x, y));
st[x][y] = true;
}
if (is_bound) bound++;
}
return total == bound;
}
static class PII {
int x;
int y;
public PII(int x, int y) {
this.x = x;
this.y = y;
}
}
}
有对代码不理解的地方可以在下方评论