关于DFS和BFS总结(java)

DFS和BFS都是两种搜索树或者图的基本策略。DFS常用于暴力搜索所有状态,BFS常用于搜索到达某一状态的最短路径

具体教程链接(我只是进行了一下重要的摘记,想要详细了解的去看这个专业版)

深度优先搜索算法(DFS)

dfs算法就是一条路走到黑,走到无路可走就会返回。这也称之为回溯。在dfs函数中,需要判断是否到达终点,然后就是对下一步进行操作,判断位置是否合法(根据题目具体要求),再对下一个点进行dfs,一般完成之后需要回溯。

2060. 奶牛选美[Acwing]

解题思路:

一开始还真被这个题目唬住了,其实还是很容易的。先dfs遍历一遍将两组斑点坐标都记录下来,再使用曼哈顿距离求解即可。(就是两个坐标的横坐标距离之差的绝对值加上纵坐标距离之差的绝对值)

代码:

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    static int n, m;
    static char[][] map = new char[55][55];
    static boolean[][] vis;

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

    static class Node {//节点类
        int x, y;

        public Node(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    public static void dfs(int x, int y, List<Node> node) {
        vis[x][y] = true;//标记当前点已访问
        node.add(new Node(x, y));//将当前点加入集合

        //遍历四个方向
        for (int i = 0; i < 4; i++) {
            int tx = x + dx[i];
            int ty = y + dy[i];
            //超出边界、是'.'、已访问过的点,跳过
            if (tx < 0 || tx >= n || ty < 0 || ty >= m || map[tx][ty] == '.' || vis[tx][ty])
                continue;

            dfs(tx, ty, node);

        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        m = scanner.nextInt();
        scanner.nextLine();
        vis = new boolean[n][m]; // 修改vis数组的大小
        for (int i = 0; i < n; i++) {
            map[i] = scanner.nextLine().toCharArray();
        }

        //初始化两组斑点集合
        List<Node>[] nodesLists = new ArrayList[2];
        nodesLists[0] = new ArrayList<>();
        nodesLists[1] = new ArrayList<>();

        //遍历地图,找到两组斑点
        for (int i = 0, k = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (map[i][j] == 'X' && !vis[i][j]) {
                    dfs(i, j, nodesLists[k++]);//dfs找到斑点,再加入对应的集合
                }
            }
        }

        int res = Integer.MAX_VALUE;

        for (Node xNode : nodesLists[0]) {
            for (Node yNode : nodesLists[1]) {
                //计算曼哈顿距离并更新结果
                res = Math.min(res, Math.abs(xNode.x - yNode.x) + Math.abs(xNode.y - yNode.y));
            }
        }

        System.out.println(res - 1); // 输出结果(需要涂几个点,因此要减一)

    }

}

广度优先搜索(BFS)

bfs算法要先搜索当前状态可以直接到达的所有状态。一般都是利用队列进行实现。

可以通过题目来体验bfs算法​​​​​​[2018年 蓝桥杯 国C]迷宫与陷阱icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P8673

解题思路:

这个题目是在普通的bfs算法上又多加了一些条件。因为每一步都需要判断无敌状态的步数,所以我们可以定义一个Node类,记录该点的行、列、步数、无敌状态步数。同时还要定义一个vis数组,记录格子是否被访问过。

然后就是bfs的核心算法。先将起点入队,在while循环中加入当前状态的其他可到达状态。这个时候就要思考其他可到达状态是哪些:如果下一个状态是陷阱'X',并且当前不是无敌步数为0,肯定是不能到达这个点的,直接跳过;如果下一个状态有无敌道具,我们就需要更新无敌步数为k;如果下一个状态只是'.'并且位置合法,我们就把这个状态的vis记录为当前的status(这里相当于加了一个剪枝:入队时需要 vis[x][y] < magic,即如果当前节点已经被访问过,且之前到达该节点时的无敌状态剩余步数比现在要多,则不需要再次访问该节点。因为如果我们之前已经访问过该节点并且使用了更多的无敌步数,那么这条路径一定不是最优解。所以vis初始时要赋值为全 −1。)最后再把这个点加入队列中。讲的有点混乱,看代码应该能更好理解一些。

代码:

import java.util.*;

public class Main {
    static final int N = 1005;
    static char[][] map = new char[N][N];//迷宫地图
    static int[][] vis = new int[N][N];//存储每个格子是否被访问过,以及无敌状态剩余步数
    static int n,k;
    
    //定义节点类
    static class Node{
    	int x,y,step,state;//行、列、步数、无敌状态
    	
    	public Node(int x, int y, int step, int state) {
    		this.x = x;
    		this.y = y;
    		this.step = step;
    		this.state = state;
    	}
	}
    
    static int[] dx = {1, -1, 0, 0};
    static int[] dy = {0, 0, 1, -1};
    
    public static void main(String[] args) {
    	Scanner scanner = new Scanner(System.in);
    	n = scanner.nextInt();
    	k = scanner.nextInt();
    	for(int i = 1; i <= n; i++) {
    		String lineString = scanner.next();
    		for(int j = 0; j < n; j++) {
    			map[i][j + 1] = lineString.charAt(j);// 读取迷宫地图
    		}
    	}
    	scanner.close();
    	
    	//初始化访问数组
    	for(int i = 1; i <= n; i++) {
    		Arrays.fill(vis[i], -1);
    	}
    	
    	Queue<Node> queue = new LinkedList<>();
    	vis[1][1] = 0;//起点访问设置为0
    	queue.offer(new Node(1,  1,  0,  0));//将起点入队,并设置其达到该点的步数为0、当前不处于无敌状态
    	
    	while(!queue.isEmpty()) {
    		Node tNode = queue.poll();//取出队头节点
    		if(tNode.x == n && tNode.y == n) {//如果当前节点为终点,则输出最短路径长度并结束程序
    			System.out.println(tNode.step);
    			return;
    		}
    		for(int i = 0; i < 4; i++) {
    			int tx = tNode.x + dx[i];
    			int ty = tNode.y + dy[i];
    			if(map[tx][ty] == 'X' && tNode.state == 0)//如果下一步是陷阱且当前不处于无敌状态,则跳过该节点
    				continue;
    			int state = Math.max(0, tNode.state - 1);//计算当前无敌状态剩余步数
    			if(map[tx][ty] == '%')//如果下一步位置有道具,更新无敌状态剩余步数
    				state = k;
    			if(tx >= 1 && tx <= n && ty >= 1 && ty <=n && vis[tx][ty] < state && map[tx][ty] != '#'){
    				//如果下一步位置是合法可以到达的
    				vis[tx][ty] = state;//更新访问状态和无敌状态剩余步数
    				queue.offer(new Node(tx, ty, tNode.step + 1, state));//将下一步位置入队,并更新到达该点的步数和无敌状态剩余步数
    			}
    		}
    	}
    		System.out.println(-1);
    	}
    }

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值