算法|题解

计蒜客 – 逃跑

凝神长老 · 3月13日 · 2019年 · 1237次已读

本文写于 2019年03月13日,距今已超过 1 年,距 2020年07月25日 的最后一次修改也已超过 3 个月,部分内容可能已经过时,您可以按需阅读。如果图片无法显示或者下载链接失效,请给我反馈,谢谢!


这是一道广搜的题,起点终点是明确的,最后求逃离迷宫的最短步数,所以是比较原始的广度优先搜索。

处理的难点在于哪些点是不能访问的,即,所谓的障碍物。

由于多了一个时间的维度,所以,记录迷宫的图、标记访问状态的数组,都将是三维的boolean数组。

g[x][y][t] 表示点 (x, y) 在时刻 t 可以踩上去,visited[x][y][t] 记录三元组的访问状态。

队列中存放的访问信息就是 (x, y, t) 的三元组。

由于有生命的限制,所以数组的第三个维度开到 d 就可以了,在广搜的任何一个时刻只要 t > d 就立刻结束搜索。

难点在于哪些点是不可访问的。

守卫站着的点是永远不能访问的。

只需要预处理所有子弹会出现的点、以及时刻。

计算子弹在给定方向上的所有可能出现的位置以及出现的时刻,标记所有这些 (x, y, p) 三元组不可访问。

然后在 p < d 的前提下每次将 p 增加子弹射击的间隔 t

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
	
	static boolean[][][] g; // 点 (x, y) 在时刻 t 是否可达
	static boolean[][][] visited; // 标记访问状态
	static int n;
	static int m;
	static int d;

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		m = in.nextInt();
		int k = in.nextInt();
		d = in.nextInt();
		g = new boolean[n + 1][m + 1][d + 1];
		visited = new boolean[n + 1][m + 1][d + 1];
		for (int i = 0; i <= n; i++)
			for (int j = 0; j <= m ;j++)
				for (int l = 0; l <= d; l++)
					g[i][j][l] = true; // 默认都可以访问
		for (int i = 0; i < k; i++) {
			String c = in.next();
			int t = in.nextInt();
			int v = in.nextInt();
			int x = in.nextInt();
			int y = in.nextInt();
			// 守卫所在的点永远不可达
			for (int j = 0; j <= d; j++) {
				g[x][y][j] = false;
			}
			// 获取射击方向
			int dx = 0, dy = 0;
			switch (c) {
			case "N":
				dx = -1;
				break;
			case "S":
				dx = 1;
				break;
			case "W":
				dy = -1;
				break;
			case "E":
				dy = 1;
				break;
			}
			int p = 1;
			// 计算子弹到达的点及到达的时间,在该时刻该点不可达
			while (p <= d) {
				int newX = x + dx * v * p;
				int newY = y + dy * v * p;
				// 计算第一颗子弹命中的位置
				if (newX >=0 && newX <= n && newY >=0 && newY <= m) {
					g[newX][newY][p] = false;
					int l = 1;
					while (p + l * t <= d) {
						// 该位置每隔 t 秒命中一次
						g[newX][newY][p + l * t] = false;
						l++;
					}
				}
				p++;
			}
		}
		// 预处理完成,开始广搜,输出答案
		int ans = bfs(0, 0, 0);
		System.out.println(ans == -1 ? "Bad luck!" : ans);
		in.close();
	}

	private static int bfs(int X, int Y, int T) {
		Queue<Point> q = new LinkedList<Point>();
		q.add(new Point(X, Y, T));
		visited[X][Y][T] = true;
		while (!q.isEmpty()) {
			Point current = q.poll();
			// 在生命耗尽之前到达出口
			if (current.getX() == n && current.getY() == m && current.getT() <= d) {
				return current.getT();
			} else if (current.getT() > d){
				// 耗时大于生命,直接死掉
				return -1;
			} else {
				for (Point neighbour : getNeighbours(current)) {
					visited[neighbour.getX()][neighbour.getY()][neighbour.getT()] = true;
					q.add(neighbour);
				}
			}
		}
		// 走投无路,死掉
		return -1;
	}
	
	private static ArrayList<Point> getNeighbours(Point p) {
		ArrayList<Point> neighbours = new ArrayList<Point>();
		int x = p.getX();
		int y = p.getY();
		int t = p.getT();
		for (int dx = -1; dx <= 1; dx++)
			for (int dy = -1; dy <= 1; dy++) {
				// 可以站在原地等 1 秒,找子弹的空隙,所以一共是 5 个邻居
				if (Math.abs(dx) != Math.abs(dy) || dx == 0 && dy == 0) {
					int xx = x + dx;
					int yy = y + dy;
					// t + 1 > d 的全部剪枝
					if (xx >= 0 && xx <= n && yy >= 0 && yy <= m && t + 1 <= d) {
						if (g[xx][yy][t + 1])
							if (!visited[xx][yy][t + 1])
							neighbours.add(new Point(xx, yy, t + 1));
					}
				}
			}
		return neighbours;
	}
}

class Point {
	private int x;
	private int y;
	private int t;
	
	public Point(int x, int y, int t) {
		super();
		this.x = x;
		this.y = y;
		this.t = t;
	}
	
	public int getX() {
		return x;
	}

	public int getY() {
		return y;
	}

	public int getT() {
		return t;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + t;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Point other = (Point) obj;
		if (t != other.t)
			return false;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}
	
	@Override
	public String toString() {
		return "Point [x=" + x + ", y=" + y + ", t=" + t + "]";
	}
	
}
订阅评论动态
提醒
guest
0 评论
行内反馈
查看所有评论