
这是一道广搜的题,起点终点是明确的,最后求逃离迷宫的最短步数,所以是比较原始的广度优先搜索。
处理的难点在于哪些点是不能访问的,即,所谓的障碍物。
由于多了一个时间的维度,所以,记录迷宫的图、标记访问状态的数组,都将是三维的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 + "]";
}
}
