蓝桥杯AcWing学习笔记 6-2宽搜BFS的学习(附相关蓝桥真题:全球变暖)(Java)

本文通过多个AcWing例题详细讲解了BFS(广度优先搜索)算法的应用,包括‘献给阿尔吉侬的花束’、‘红与黑’和‘地牢大师’等题目。通过BFS解决迷宫问题、连通块计数和最短路径等挑战,阐述了BFS的基本思想和模板,并展示了如何在Java中实现。同时,介绍了FloodFill算法及其在二维和三维空间的应用。
摘要由CSDN通过智能技术生成

有参加蓝桥杯的同学可以给博主点个关注,博主也在准备蓝桥杯,可以跟着博主的博客一起刷题。

蓝桥杯

我的AcWing

题目及图片来自蓝桥杯C++ AB组辅导课

BFS

栈 → DFS

队列 → BFS

image-20220223110444193

深搜dfs,一搜到底;宽搜bfs,一层一层的遍历。

queue: [] 每次取出队首元素,将拓展出的所有元素放到队尾。

下图就是队列取出元素及放入元素:

image-20220223111206522

我们可以发现,入队的顺序就是层序遍历的顺序。

宽搜模板

① 判重数组 st[] 入队时判重

queue

伪代码模板

queue ← 初始状态
while (queue非空) {
	t ← 队首
	for (拓展t) {
		ver ← 新节点
		if (!st[ver]) { // 判重
			ver → 队尾
		}
	}
}

例题

AcWing 1101. 献给阿尔吉侬的花束

模板题

第一个样例:

image-20220223120539308

需要走5步,答案应该是5。

第三个样例:

image-20220223120606626

被墙挡住了,无法吃到奶酪,输出“oop!”。

我们用bfs遍历:

image-20220223121330529

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

有多少个'.'和'@'相连

从起点出发,向四周进行广度优先遍历搜索;一圈一圈来扩,每一次取出来队首没有被扩过的点。

image-20220223225717820

套用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个偏移量来表示:

image-20220227170310879

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算法。

第一个样例,被蓝色划掉的陆地会被淹没掉:

image-20220227171932376

求会被完全淹没的岛屿数量,红色会被完全淹没,绿色还会剩一个陆地。

image-20220227172432107

宽搜:从起点出发,向四周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,则表示这个岛屿会被完全淹没。

image-20220227173628308

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

有对代码不理解的地方可以在下方评论

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小成同学_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值