算法笔记方法论8 广度优先搜索BFS

题目24 抓住那头牛

描述

农夫知道一头牛的位置,想要抓住它。农夫和牛都位于数轴上,农夫起始位于点N(0<=N<=100000),牛位于点K(0<=K<=100000)。农夫有两种移动方式:

1、从X移动到X-1或X+1,每次移动花费一分钟

2、从X移动到2*X,每次移动花费一分钟

假设牛没有意识到农夫的行动,站在原地不动。农夫最少要花多少时间才能抓住牛?

输入

两个整数,N和K

输出

一个整数,农夫抓到牛所要花费的最小分钟数

样例输入

5 17

样例输出

4
public class _24抓住那头牛 {
    
    static int N, K;
    static int MAXN = 100000;
    static int[] visited = new int[MAXN + 10]; // 判重标记,visited[i] = true表示i已经扩展过

    static class Step{
        int x; // 位置
        int steps; // 到达x所需的步数

        public Step(int x, int steps) {
            this.x = x;
            this.steps = steps;
        }
    }

    static Queue<Step> q = new LinkedList<Step>(); // 队列,即open表

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        K = sc.nextInt();
        q.offer(new Step(N, 0));
        visited[N] = 1;

        while (!q.isEmpty()) {
            Step s = q.peek(); // 查看队头元素
            if (s.x == K) {
                System.out.println(s.steps);
                return ;
            } else {
                if (s.x - 1 >= 0 && visited[s.x-1] == 0) {
                    q.offer(new Step(s.x-1,s.steps+1));
                    visited[s.x-1] = 1;
                }
                if (s.x + 1 <= MAXN && visited[s.x+1] == 0) {
                    q.offer(new Step(s.x+1, s.steps+1));
                    visited[s.x+1] = 1;
                }
                if (s.x*2 <= MAXN && visited[s.x*2] == 0) {
                    q.offer(new Step(s.x*2, s.steps+1));
                    visited[s.x*2] = 1;
                }
                q.poll(); // 队头元素出队
            }
        }
    }
}

题目25 迷宫问题

Description

定义一个二维数组:
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表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
Input

一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。
Output

左上角到右下角的最短路径,格式如样例所示。
Sample Input

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
Sample Output

(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)

public class _25迷宫问题 {
    
    static class Point {
        public int x;
        public int y;
        public Point(int x,int y)
        {
            this.x = x;
            this.y = y;
        }
    }
    public static int[][] map = new int[6][6];
    public static Point[][] address = new Point[6][6];
    public static Queue<Point> queue = new LinkedList();
    public static void main(String[] args)
    {
        Scanner in = new Scanner(System.in);
        for(int i=0 ; i<5 ; i++)
        {
            for(int j=0 ; j<5 ; j++)
            {
                map[i][j] = in.nextInt();
            }
        }
        bfs();
        show();
    }
    public static void bfs()
    {
        int[] dx = {0,0,1,-1};
        int[] dy = {1,-1,0,0};
        queue.add(new Point(4,4));
        while(!queue.isEmpty())
        {
            Point p = queue.peek();
            queue.poll();

            if(p.x==0&&p.y==0)break;
            for(int i=0 ; i<4 ; i++)
            {
                int newX = p.x+dx[i];
                int newY = p.y+dy[i];
                // 超出边界
                if(newX<0 || newY<0 || newX==5 || newY==5) {
                    continue;
                }
                // 已访问过
                if(map[newX][newY] == 1) {
                    continue;
                }
                map[newX][newY]=1;
                address[newX][newY]=p;
                queue.add(new Point(newX,newY));
            }
        }
        queue.clear();
    }
    public static void show()
    {
        int newX = 0;
        int newY = 0;
        while(true)
        {
            System.out.printf("(%d, %d)\n",newX, newY);
            if(newX == 4 && newY == 4) {
                break;
            }
            int tmp = newX; // ? 回溯
            newX = address[newX][newY].x;
            newY = address[tmp][newY].y;
        }
    }
}

题目26 鸣人和佐助

题目:
已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置。鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动,每移动一个距离需要花费1个单位时间,打败大蛇丸的手下不需要时间。如果鸣人查克拉消耗完了,则只可以走到没有大蛇丸手下的位置,不可以再移动到有大蛇丸手下的位置。佐助在此期间不移动,大蛇丸的手下也不移动。请问,鸣人要追上佐助最少需要花费多少时间?

输入
输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T。0 < M,N < 200,0 ≤ T < 10
后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。
输出
输出包含一个整数R,代表鸣人追上佐助最少需要花费的时间。如果鸣人无法追上佐助,则输出-1。

样例输入1
4 4 1
#@##
**##
###+
****
样例输入2
4 4 2
#@##
**##
###+
****
样例输出1
6
样例输出2
4
public class _26鸣人和佐助 {

    static int M, N, T;
    static int MAXN = 210;
    static char[][] a = new char[MAXN][MAXN];
    static class Pos {
        int r,c,ck,steps;

        public Pos() {
        }

        public Pos(int r, int c, int steps, int ck) {
            this.r = r;
            this.c = c;
            this.steps = steps;
            this.ck = ck;
        }
    }

    static Queue<Pos> q = new LinkedList<Pos>();
    // used[r][c][ck]表示位于(r,c)且查克拉数量为ck这种状态是否已被扩展
    static int[][][] used = new int[MAXN][MAXN][15];
    Pos s, e; // 起点和终点

    // 定义方向数组
    static int[][] dir = {{0, 1}, // 向右
                         {1, 0},  // 向下
                         {-1, 0}, // 向上
                         {0, -1}}; // 向左

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Pos s = new Pos();
        Pos e = new Pos();
        M = sc.nextInt();
        N = sc.nextInt();
        T = sc.nextInt();

        for (int i=1; i<=M; ++i) {
            String s1=sc.next();
            for (int j=1; j<=N; ++j) {
                a[i][j] = s1.charAt(j-1);
                if (a[i][j] == '@') {
                    s = new Pos(i, j, 0, T);
                } else if (a[i][j] == '+') {
                    a[i][j] = '*';
                    e = new Pos(i, j,-1,-1);
                }
            }
        }
        used[s.r][s.c][T] = 1;
        q.offer(s);

        while (!q.isEmpty()) {
            Pos p = q.peek();
            if (p.r == e.r && p.c == e.c) {
                System.out.println(p.steps);
                return;
            }
            q.poll();
            for (int i=0; i<4; ++i) {
                int newR = p.r + dir[i][0];
                int newC = p.c + dir[i][1];
                //遇到通路 不使用查克拉
                if (a[newR][newC] == '*' && used[newR][newC][p.ck] == 0) {
                    used[newR][newC][p.ck] = 1;
                    q.offer(new Pos(newR, newC, p.steps+1, p.ck));
                }
                // 有大蛇丸手下 使用查克拉
                if (a[newR][newC] == '#' && p.ck > 0 && used[newR][newC][p.ck-1] == 0) {
                    used[newR][newC][p.ck] = 1;
                    q.offer(new Pos(newR, newC, p.steps+1,p.ck-1));
                }
            }

        }
        System.out.println("-1");
    }
}

广搜一般用于状态表示比较简单、求最优策略的问题

优点:是一种完备策略,即只要问题有解,它就一定可以找到解 。并且,广度优先搜索找到的解,还一定是路径最短的解。

缺点:盲目性较大,尤其是当目标节点距初始节点较远时,将产 生许多无用的节点,因此其搜索效率较低。需要保存所有扩展出 的状态,占用的空间大

深搜几乎可以用于任何问题

只需要保存从起始状态到当前状态路径上的节点。

根据题目要求凭借自己的经验和对两个搜索的熟练程度做出选择。

你可能感兴趣的:(算法笔记,算法,蓝桥杯,bfs,dfs)