Java算法_BFS和优先队列——HDU 1072:Nightmare

BFS

 图遍历算法是指对图中(Graph)每一个顶点(Vertex)都访问一次,且仅仅只访问一次。而对顶点遍历的顺序的不同,导致了不同的算法。而BFS则是以宽度优先进行的图遍历算法。所谓以宽度优先指的是每一步先遍历顶点的所有相邻顶点,然后再往相邻顶点的相邻顶点进行延申。

HDU 1072:Nightmare

http://acm.hdu.edu.cn/showproblem.php?pid=1072

Problem Description

Ignatius had a nightmare last night. He found himself in a labyrinth with a time bomb on him. The labyrinth has an exit, Ignatius should get out of the labyrinth before the bomb explodes. The initial exploding time of the bomb is set to 6 minutes. To prevent the bomb from exploding by shake, Ignatius had to move slowly, that is to move from one area to the nearest area(that is, if Ignatius stands on (x,y) now, he could only on (x+1,y), (x-1,y), (x,y+1), or (x,y-1) in the next minute) takes him 1 minute. Some area in the labyrinth contains a Bomb-Reset-Equipment. They could reset the exploding time to 6 minutes.

伊格内修斯昨晚做了一个恶梦。他发现自己在一个迷宫里,身上有一颗定时炸弹。迷宫有出口,伊格内修斯应该在炸弹爆炸前离开迷宫。炸弹的初始爆炸时间被设定为6分钟。为了防止炸弹震动而爆炸,伊格那丢斯必须缓慢移动,也就是从一个区域移动到最近的区域(也就是说,如果伊格那丢现在站在(x,y)上,他只能在(x+1,y)、(x-1,y)、(x,y+1)或(x,y-1)下一分钟内移动)需要1分钟。迷宫里有个重置炸弹的装置。他们可以把爆炸时间设定为六分钟。

Given the layout of the labyrinth and Ignatius’ start position, please tell Ignatius whether he could get out of the labyrinth, if he could, output the minimum time that he has to use to find the exit of the labyrinth, else output -1.

给定迷宫的布局和伊格内修斯的起始位置,告诉伊格内修斯他是否可以走出迷宫,如果可以,输出他找到迷宫出口所需要的最短时间,否则输出-1。

Here are some rules:

  1. We can assume the labyrinth is a 2 array.我们可以假设迷宫是两个数组
  2. Each minute, Ignatius could only get to one of the nearest area, and he should not walk out of the border, of course he could not walk on a wall, too.每一分钟,伊格内修斯只能走到最近的一个地方,他不应该走出边境,当然他也不能走在墙上
  3. If Ignatius get to the exit when the exploding time turns to 0, he can’t get out of the labyrinth.如果伊格内修斯在爆炸时间为0时到达出口,他就无法逃出迷宫
  4. If Ignatius get to the area which contains Bomb-Rest-Equipment when the exploding time turns to 0, he can’t use the equipment to reset the bomb.如果Ignatius在爆炸时间为0时到达了含有炸弹休息设备的区域,他就不能使用设备来重置炸弹
  5. A Bomb-Reset-Equipment can be used as many times as you wish, if it is needed, Ignatius can get to any areas in the labyrinth as many times as you wish.一个炸弹重置设备可以使用多次,如你希望,如果需要,伊格内修斯可以到达迷宫的任何区域多次,如你所愿
  6. The time to reset the exploding time can be ignore, in other words, if Ignatius get to an area which contain Bomb-Rest-Equipment, and the exploding time is larger than 0, the exploding time would be reset to 6.可以忽略重置爆炸时间的时间,也就是说,如果伊格内修斯到达含有炸弹休息设备的区域,爆炸时间大于0,则将爆炸时间重置为6

Input

The input contains several test cases. The first line of the input is a single integer T which is the number of test cases. T test cases follow.
Each test case starts with two integers N and M(1<=N,Mm=8) which indicate the size of the labyrinth. Then N lines follow, each line contains M integers. The array indicates the layout of the labyrinth.

输入包含几个测试用例。输入的第一行是一个整数T,也就是测试用例的数量。下面是T个测试用例。
每个测试用例以两个整数N和M(1<=N,Mm=8)开始,表示迷宫的大小。然后是N行,每一行包含M个整数。数组表示迷宫的布局。

There are five integers which indicate the different type of area in the labyrinth:
0: The area is a wall, Ignatius should not walk on it.
1: The area contains nothing, Ignatius can walk on it.
2: Ignatius’ start position, Ignatius starts his escape from this position.
3: The exit of the labyrinth, Ignatius’ target position.
4: The area contains a Bomb-Reset-Equipment, Ignatius can delay the exploding time by walking to these areas.

有五个整数表示迷宫中不同类型的区域:
0:这个区域是一堵墙,伊格内修斯不应该在上面走。
1:这个区域什么都没有,伊格内修斯可以在上面行走。
2:伊格内修斯的开始位置,伊格内修斯开始从这个位置逃跑。
3:迷宫的出口,伊格内修斯的目标位置。
4:该区域包含一个炸弹重置设备,伊格内修斯可以通过步行到这些区域来延迟爆炸时间。

Output

For each test case, if Ignatius can get out of the labyrinth, you should output the minimum time he needs, else you should just output -1.

对于每个测试用例,如果Ignatius能够走出迷宫,您应该输出他所需的最小时间,否则您应该只输出-1。

Sample Input

3
3 3
2 1 1
1 1 0
1 1 3
4 8
2 1 1 0 1 1 1 0
1 0 4 1 1 0 4 1
1 0 0 0 0 0 0 1
1 1 1 4 1 1 1 3
5 8
1 2 1 1 1 1 1 4 
1 0 0 0 1 0 0 1 
1 4 1 0 1 1 0 1 
1 0 0 0 0 3 0 1 
1 1 4 1 1 1 1 1 

Sample Output

4
-1
13
package com.hhhyixin.book.cp04.HDU_1072;

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {

    int T, m, n, start_x, start_y;
    int[][] map;
    PriorityQueue<Node> queue;
    int[] fx = {1, 0, 0, -1};//fx[1]fy[0]向右移动、fx[0]fy[-1]向下移动、fx[0]fy[1]向上移动、fx[-1]fy[0]向左移动
    int[] fy = {0, -1, 1, 0};
    Node node;

    public static void main(String[] args) {
        new Main().solve();
    }

    public void solve() {
        Scanner in = new Scanner(System.in);
        T = in.nextInt();   //输入包含几个测试用例
        while ((T--) != 0) {
            //输入迷宫矩阵有多少行多少列
            //n行
            n = in.nextInt();
            //m列
            m = in.nextInt();
            map = new int[n][m];
            queue = new PriorityQueue<>(cNode); //用到优先队列
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    map[i][j] = in.nextInt();   //一个一个输入迷宫矩阵的数值
                    if (map[i][j] == 2) {   //当输入的是2的时候,说明这个位置是开始的位置
                        //记录这个位置的坐标
                        start_x = i;
                        start_y = j;
                    }
                }
            }
            //开始走迷宫
            node = new Node(start_x, start_y, 0, 0);    //Node类记录开始时的坐标,记录步数和时间
            queue.add(node);    //把node添加到优先队列中保存
            if (BFS()) {    //如果广度优先搜索完之后返回true,步数加1
                System.out.println(node.step + 1);
            } else {    // 如果返回flase,直接输出-1,结束
                System.out.println(-1);
            }

        }
    }

    private boolean BFS() {
        while (!queue.isEmpty()) {  //当优先队列中不是空值时,进循环
            node = queue.poll();    //取出数据并删除
            for (int i = 0; i < 4; i++) {
                //每循环一次,向上/下/左/右走一步,步数和时间分别加1
                int x_ = node.x + fx[i];
                int y_ = node.y + fy[i];
                int time_ = node.time + 1;
                int step_ = node.step + 1;
                Node t = new Node(x_, y_, step_, time_);
                if (check(t)) { //每走一步都检查一下是否到出口
                    if (map[x_][y_] == 3) {//找到出口
                        return true;
                    }
                    queue.add(t);   //没有找到出口就把当前位置、步数和时间加到队列里面
                }
            }
        }//当取出来node中最后一组数据进行BFS,while循环到最后都没有返回true的话,那就是没有找到3(迷宫出口)
        return false;
    }

    private boolean check(Node t) {
        if (t.x < 0 || t.y < 0 || t.x > n - 1 || t.y > m - 1 || map[t.x][t.y] == 0)
            return false;
        if (t.time == 6)
            return false;
        if (map[t.x][t.y] == 4) {   //走到4爆炸时间清零
            t.time = 0;
            map[t.x][t.y] = 0;
        }
        return true;
    }

    //step较小的先返回
    static Comparator<Node> cNode = new Comparator<Node>() {
        public int compare(Node o1, Node o2) {
            return o1.step - o2.step;
        }

    };

}

class Node {
    //x,y记录位置
    //step记录步数
    //time记录时间,大于等于6秒的不行
    int x;
    int y;
    int step;
    int time;

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

image-20220923134124460

思路:使用到优先队列,当前这个点的步数(从起点算起)小的优先,然后到达4以后,需要把4重置为0,即4这个点原则上只能来一次,因为如果重复的来4,虽然炸弹也不会爆炸,但是对于解题没有意义。其他的基本上就是普通的BFS的思路了。

Comparator

comparator 是javase中的接口,位于java.util包下;public interface Comparator

该接口代表一个比较器,比较器具有可比性,sort方法sort就是使用Comparator接口来处理排序的,大家见久了都认为Comparator接口是用来排序的

作为接口Comparator提供了两个抽象方法

类型方法
intcompare(T o1, T o2) 比较用来排序的两个参数。
booleanequals(Object obj) 指示某个其他对象是否“等于”此 Comparator。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

要什么自行车儿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值