BFS进阶——大胖子走迷宫

本文讨论了一个扩展迷宫问题,主角大胖子的体积会随时间变化,需要判断更大范围的相邻格子是否可达。作者使用BFS算法,通过变量nr表示体积变化阶段,处理了不同阶段的判定范围。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大胖子走迷宫
题目分析

这个是迷宫题的拓展,本质还是一样的。那么不同之处在于这个人占的面积。一开始他是5*5的大小,在正常情况下,我判断一个人可不可以走向下一个格子,我只需要判断下一个格子能不能走即可,但是现在这种情况,我需要判断5*5的格子能不能走。那么也就是下一个要走的格子以及它的上下左右两步内的邻居都要判断,即

(x-2,y-2)(x-2,y-1)(x-2,y)(x-2,y+1)(x-2,y+2)
(x-1,y-2)(x-1,y-1)(x-1,y)(x-1,y+1)(x-1,y+2)
(x,y-2)(x,y-1)中心点(x,y)(x,y+1)(x,y+2)
(x+1,y-2)(x+1,y-1)(x+1,y)(x+1,y+1)(x+1,y+2)
(x+2,y-2)(x+2,y-1)(x+2,y)(x+2,y+1)(x+2,y+2)

通过上述表格可以看出我们整个要判断的范围其实是从(x-2,y-2)到(x+2,y+2)。

下一阶段,经过了t时刻,它的体积会变成3*3的大小,此时我们要判断的其实就是下一个要走的格子以及它的上下左右一步内的邻居都要判断,即

(x-1,y-1)(x-1,y)(x-1,y+1)
(x,y-1)中心点(x,y)(x,y+1)
(x+1,y-1)(x+1,y)(x+1,y+1)

通过上述表格可以看出我们整个要判断的范围其实是从(x-1,y-1)到(x+1,y+1)。

再下一阶段,经过了2t时刻,它的体积会变成1*1的大小,就是正常大小。那么判断范围就变成了(x-0,y-0)到(x+0,y+0),其实就是只判断(x,y)。

通过上述分析我们可以发现,范围的那个变量就是x和y加或者减去的那个值从2变成了1,又从1变成了0.那么我们可以用一个变量来表示它,即nr。nr=2时表示我此时的体积是5*5,nr=1时表示我此时的体积是3*3,nr=0时表示我此时的体积是1*1。bfs代码如下

取出队中的节点,首先判断我是否到达了终点,到达了就打印答案,然后退出。

private static void bfs() {
	while(queue.size()!=0) {
		Node node = queue.poll();
		int x = node.x;
		int y = node.y;
		int t = node.t;
		if(x == n-3 &&y== n-3) {
			System.out.println(t);
			return;
		}

注意我们题目中说了,我可以原地不动,所以我要把这种走法考虑进去,而我原地不动的目的其实是为了消耗脂肪,变成比原来更小的体积,如果我体积已经是1*1了那么我没必要做这个操作,所以我有一个判断,t是走到这一步过去的时间,如果它大于等于k的2倍其实就是说明他已经变成了1*1,所以只有t < 2*k时,才进行该操作。

//这个if语句是干什么的  停留在原地不动  是为了消耗脂肪 变小
if(t / k < 2) {
    queue.add(new Node(x, y, t+1));
}

接下来就是向四个方向遍历,这里和普通版本的思路一致,稍微复杂点的地方是我要判断很多个格子是否都可以走。这里变量nr的作用在前面解释过了,如果当前的时间大于等于2*k,那么nr=0,如果当前的时间大于等于k,那么nr=1,否则nr=2。当然,在遍历的时候我们是从(nx-nr,ny-nr)遍历到(nx+nr,ny+nr),那么我们要注意是否会越界。

for (int i = 0; i < 4; i++) {
    int nx = x + d[i][0];
    int ny = y + d[i][1];
    int nr;
    if(t>=2*k) nr = 0;
    else if(t>=k) nr = 1;
    else nr = 2;
    if(nx-nr<0 || nx+nr>=n || ny-nr<0 || ny+nr>=n||visit[nx][ny]==1) {
        continue;
    }
    boolean ok = true;//记录5*5或者3*3或者1*1的格子里是否有障碍物
    //与迷宫的不同之处就在于 判断障碍物的时候不知判断nx,ny位置
    for (int j = nx-nr; j <= nx+nr; j++) {
        for (int k = ny-nr; k <= ny+nr; k++) {
            if(map[j][k] == '*') {
                ok = false;
                break;
            }
        }
    }

最后,遍历完了都可以走,那么就走向下一个节点,并且将下一个节点入队,

if(ok) {
    visit[nx][ny] = 1;
    queue.add(new Node(nx, ny, t+1));
}
题目代码
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Scanner;
public class Main{
	/*
	 * 小明的体积在走迷宫的过程中有什么影响。
	 */
	static int flag=0;
	static int[][] visit;
	static int[][]  d = {{0,1},{0,-1},{1,0},{-1,0}};
	static char[][] map; 
	static int[] r = {2,1,0};
	static Queue<Node> queue = new LinkedList<Node>();
	static int n;
	static int k;
	static class Node{
		int x;
		int y;
		int t;
		public Node(int x, int y, int t) {
			super();
			this.x = x;
			this.y = y;
			this.t = t;
		}		
	}
public static void main(String[] args) {
	Scanner scanner = new Scanner(System.in);
	n = scanner.nextInt();
	k = scanner.nextInt();
	map = new char[n][n];
	visit = new int[n][n];
	for (int i = 0; i < map.length; i++) {
		map[i] = (scanner.next()).toCharArray();
	}
	queue.add(new Node(2, 2, 0));
	visit[2][2]=1;
	bfs();
	System.out.println();
}
private static void bfs() {
	while(queue.size()!=0) {
		Node node = queue.poll();
		int x = node.x;
		int y = node.y;
		int t = node.t;
		if(x == n-3 &&y== n-3) {
			System.out.println(t);
			return;
		}
		//这个if语句是干什么的  停留在原地不动  是为了消耗脂肪 变小
		if(t / k < 2) {
			queue.add(new Node(x, y, t+1));
		}		
		for (int i = 0; i < 4; i++) {
			int nx = x + d[i][0];
			int ny = y + d[i][1];
			int nr = (t / k) > 2 ? 0 : r[t/k];//根据t和k的关系获取nr的大小
			if(nx-nr<0 || nx+nr>=n || ny-nr<0 || ny+nr>=n||visit[nx][ny]==1) {
				continue;
			}
			boolean ok = true;//记录5*5或者3*3或者1*1的格子里是否有障碍物
			//与迷宫的不同之处就在于 判断障碍物的时候不知判断nx,ny位置
			for (int j = nx-nr; j <= nx+nr; j++) {
				for (int k = ny-nr; k <= ny+nr; k++) {
					if(map[j][k] == '*') {
						ok = false;
						break;
					}
				}
			}
			if(ok) {
				visit[nx][ny] = 1;
				queue.add(new Node(nx, ny, t+1));
			}
		}
	}
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值