广度优先检索——BFS

查找

伪代码描述

这里写图片描述
对应后面bfs方法的部分

广度优先搜索,先找近处,再找远处,如果把所有的相连接点列出来,会先将第一层遍历出来,再将第二,第三,第N层遍历出来 广度优先搜索可以用于对图的遍历,但它的应用远不仅如此。

HDU 1253胜利大逃亡

Ignatius被魔王抓走了,有一天魔王出差去了,这但是Ignatius逃亡的好机会.

魔王住在一个城堡里,城堡是一个A*B*C的立方体,能够被表示成A个B*C的矩阵,刚開始Ignatius被关在(0,0,0)的位置,
离开城堡的门在(A-1,B-1,C-1)的位置,如今知道魔王将在T分钟后回到城堡,
Ignatius每分钟能从一个坐标走到相邻的六个坐标中的当中一个.如今给你城堡的地图,
请你计算出Ignatius是否能在魔王回来前离开城堡(仅仅要走到出口就算离开城堡,
假设走到出口的时候魔王刚好回来也算逃亡成功),假设能够请输出须要多少分钟才干离开,假设不能则输出-1.
这里写图片描述
输入数据的第一行是一个正整数K,表明測试数据的数量.每组測试数据的第一行是四个正整数A,B,C和T(1<=A,B,C<=50,1<=T<=1000),它们分别代表城堡的大小和魔王回来的时间.然后是A块输入数据(先是第0块,然后是第1块,第2块……),每块输入数据有B行,每行有C个正整数,代表迷宫的布局,当中0代表路,1代表墙.(假设对输入描写叙述不清楚,能够參考Sample Input中的迷宫描写叙述,它表示的就是上图中的迷宫)

特别注意:本题的測试数据很大,请使用scanf输入,我不能保证使用cin能不超时.在本OJ上请使用Visual C++提交.
Output

对于每组測试数据,假设Ignatius可以在魔王回来前离开城堡,那么请输出他最少须要多少分钟,否则输出-1.
Sample Input

3 3 4 20
0 1 1 1
0 0 1 1
0 1 1 1
1 1 1 1
1 0 0 1
0 1 1 1
0 0 0 0
0 1 1 0
0 1 1 0
Sample Output

11

单输入输出版

分析这个题,本例是一个三维迷宫,每个点都用三维坐标(x,y,z)表示。 主人公每次可以前后左右上下移动,从坐标上看就是
(x+1,y,z) (x-1,y,z) (x,y+1,z) (x,y-1,z) (x,y,z+1) (x,y,z-1)
把边界情况,越界,碰墙剔除

这是一个高级的查找题目

查找的三要素

一、查找空间

不同于简单的查找,结果仅是一个数或者几个数。本题的查找空间为从点(0,0,0,)到点(a-1,b-1,c-1)合法行走路径

二、查找目标

在查找空间直接拍哪个所有的路径中寻找一条最短的路径

三、查找方法

与以往的查找方法相比,在广度优先搜索中的查找方法变的有些特殊,它不在机械地、暴力的遍历查找空间中的所有路径,而采用了某种策略

根据具体题目分析三大要素

一、将对路径的搜索转变为对状态的搜索,每个点的状态就对应四元组(x,y,z,t).(x,y,z)为坐标 t为到该点需要花费的时间

二、状态组中是否有这样一个四元组(x,y,z,t) x,y,z分别等于a-1,b-1,c-1 而且t最小

三、我们实际上并不是在原始查找查找空间进行查找的,而是在人为定义的状态是进行所需要的查找 其查找方法是针对状态来定义的。我们通过状态的扩展转移来遍历查找所有的状态、 具体方法如下

我们都知道任意一个位置(x,y,z)走到下一个位置可扩展6个状态
(x+1,y,z) (x-1,y,z) (x,y+1,z) (x,y-1,z) (x,y,z+1) (x,y,z-1)
将初始节点设为根节点,将每个扩展态视为孩子节点,那么状态转移与生成就呈现出了树的形态
这里写图片描述
将这棵包含搜索空间中所有状态的树称为解答树,采用的搜索方法实际就是在对这棵解答书进行遍历时所采用的的方法

BFS就是在遍历解答树时使每次状态转移时扩展出尽可能多的新状态,按层次遍历,先有根节点扩展出所有深度为1的节点,
再由每一个深度为1的节点扩展出每一个深度为2的节点,以此类推(且先被扩展的节点其深度不大于后扩展节点的深度),
深度与行走时间等价。
这样,当搜索过程第一次查找到状态终点的节点,其记录的时间即为所需的最短时间。(参照括号的内容理解)

剪枝

但是,即使这样查找结果依旧是非常多,最坏情况下(每个节点都可以扩张),走10步状态书就会达到6^10,
必须要采取一定的措施来制约状态的无限扩展,这种措施称之为剪枝。

剪枝顾名思义就是减去解答树中不可能存在我们需要答案的字数,从而大大减少所需查找的状态总数
(重点)
本题中,到达任意一个中间节点所用的时间都是起点到这个节点的最短时间。在搜索过程中,若有状态(x,y,z,t)
其中t不是从起点到达这个节点的最短时间,那么,我们所需要的答案不可能由该状态进行若干次扩展得到。
等价于在解答树上,我们所需要查找的节点的状态节点必不可能在该状态节点的子树上。
根据这个结论,而且BFS中先查找到的状态深度必不大于查找后的状态深度(深度与状态中的耗时成正比)
所以包含每个立方体中的坐标的状态最多被扩展一次。
查找总数就变成了a*b*c。在可接受范围内

class Node{
    int x,y,z;//位置(三维)
    int t;//所需时间
    public Node(int x, int y, int z, int t) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.t = t;
    }
}
public class 胜利大逃亡 {
    /*
     * 为了实现各个状态按照其查找顺序依次转移扩展,我们需要使用一个队列。
     * 即每次扩展得到的新状态放入队列中,待排在前面的状态都被扩展完之后,
     * 该状态才能得到扩展
     */
    static Queue<Node> q=new LinkedList<Node>();
    /*
     * 为了防止对无效状态的搜索,用一个标记数组mark[x][y][z],
     * 当已经得到过包含坐标(x,y,z)的状态后,即把mark[x][y][z]置为true,
     * 当下次再由某状态扩展出该坐标的状态时,直接丢弃。
     */
    static boolean mark[][][]=new boolean[50][50][50];//标记数组
    static int maze[][][]=new int[50][50][50];//保存迷宫(三维地图)信息
    static int go[][]={//位置变换坐标 由坐标(x,y,z)扩展得到的新坐标均可通过
                //(x+go[i][0]);y+go[i][1];z+go[i][2]得到
                {1,0,0},
                {-1,0,0},
                {0,1,0},
                {0,-1,0},
                {0,0,1},
                {0,0,-1}
            };
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        int a=input.nextInt();
        int b=input.nextInt();
        int c=input.nextInt();
        int t=input.nextInt();
        for (int i = 0; i < a; i++) {
            for (int j = 0; j < b; j++) {
                for (int k = 0; k < c; k++) {
                    maze[i][j][k]=input.nextInt();//记录地图
                    mark[i][j][k]=false;//init 标记数组
                }
            }
        }
        mark[0][0][0]=true;//标记起点
        Node start=new Node(0, 0, 0, 0);//初始状态
        q.add(start);//将初始态放入队列
        int result=BFS(a,b,c);
        System.out.println(result<=t?result:-1);
    }
    public static int BFS(int a,int b,int c){//广度优先搜索,返回耗时最小
        while (!q.isEmpty()) {//队列不为空 继续循环
            Node cur=q.poll();//拿到栈顶元素
            for (int i = 0; i < go.length; i++) {//扩展状态,也就是移动一步
                int nx=cur.x+go[i][0];
                int ny=cur.y+go[i][1];
                int nz=cur.z+go[i][2];
                if (nx<0||nx>=a||ny<0||ny>=b||nz<0||nz>=c) {//越界则丢弃
                    continue;
                }
                if (maze[nx][ny][nz]==1) {
                    continue;//遇墙则丢弃
                }
                if (mark[nx][ny][nz]==true) {//如果该状态坐标已经得到过,丢弃该状态
                    continue;//只扩展一次 这是剪枝操作
                }
                Node temp=new Node(nx,ny,nz,cur.t+1);//得到新状态,时间+1
                q.add(temp);
                mark[nx][ny][nz]=true;//标记该坐标
                if (nx==a-1&&ny==b-1&&nz==c-1) {//如果该点为终点坐标,直接返回耗时
                    return temp.t;
                }
            }

        }

        return -1;//没找到路
    }
}

总结

与动态规划题目一样,广度优先搜索的关键也是确定状态
上使用BFS之前要进行剪枝,然后判断复杂度是否符合要求
广度优先搜索的关键字
一、状态。我们确定求解问题中的状态,通过状态转移扩展,查找遍历所有状态,从而得到我们需要的答案。
二、状态扩展方式。在BFS中,我们总是尽可能的扩展状态,并将先扩展得出的状态先进行下一次扩展。
在解答树上表现为按层次遍历所有状态
三、有效状态。对于有些状态我们并不对其进行再一次扩展,而是直接舍弃它,因为根据问题分析可知,
目标状态不会由这些状态经过若干次扩展得到,即目标状态不可能存在其在解答树上的子树上,所以直接舍弃。
(剪枝操作)
四、队列。为了实现先得出的状态先进行扩展,我们使用队列,将得到的状态依次放入队尾,每次取队头元素进行扩展。
五、标记、为了判断哪些位置是有效的,我们要使用标记。
六、有效状态数。问题中的有效状态树与算法的时间复杂度同数量级,所以在算法之前必须估算其是否在我们可接受的范围内
七、最优。BFS常用于解决解决最优值问题,因为其搜索到的状态总是按照某个关键字递增(如上题最短时间)
这个特征非常适合求解最优值的问题,所以问题中一旦出现最少,最短,最优等关键字,我们就要考虑用BFS

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值