前言:作者因为懒,第一次看到题目想用简单的方法解决,在比赛过程中没想出来,只能用暴力搜索了
题目描述
众所周知,出题人没玩过双人成行,所以出了这道题
你一觉醒来,发现你和另一个时空的你被困在 n∗mn*mn∗m 大小矩形孤岛的 (x,y)(x, y)(x,y) 地块上
在地图中最多包含 平地,陷阱和传送门 三种不同地块
你和另外一个时空的你都可以上下左右移动到相邻的地块中
可是你和外一个时空的你只能同时以相反的方向移动
两人均不能跨过边界,即到达孤岛外的地方;任意人到达陷阱处会立刻死亡
现在,你能否给出一个移动序列,使得两人均能从传送门离开,其中任意一人到达传送门后一定会离开且不会再回到该孤岛中;
如果有,请输出该序列的最短长度、反之输出 -1
输入描述:
第一行四个正整数 n,m,x,y
接下来 nn 行,每行一个长度为 m 的字符串
1≤n,m≤2×10^3; 1≤x≤n; 1≤y≤m
数据保证
字符串仅包含 .#@ 三种字符 .(平地) #(陷阱) @(传送门)
保证 (x,y) 位置是平地.
输出描述:
输出一个整数
若能离开,请请输出该序列的最短长度
反之输出 -1
看到题目显然是bfs的典型题,对于对于输入数据大小我们可以最大可以给出O(n^3)的时间复杂度
显然难点在于如何处理正反方向的异步到达传送门.
我们可以从传送门出发,bfs每一个点到达附近传送点的最小距离,记录数组dis[][]
然后就是从起点开始bfs搜索出先到达的点用vis[x][y][op]记录到达(x,y)时的距离,op代表正反方向的状态(1:反,0:正),只要搜索到了某个先到达了传送点,就可以算出总的距离vis[x1][y1][op]+dis[x2][y2]其中(x1,y1)与(x2,y2)为相反走的坐标,然后就是入队,出队遍历所有结果,筛选出最优解
提供AC代码
package module;
import java.io.*;
import java.util.*;
public class mud2 {
static int[] dx1 = {0, 1, 0, -1};
static int[] dy1 = {1, 0, -1, 0};
static int[] dx2 = {0, -1, 0, 1};
static int[] dy2 = {-1, 0, 1, 0};
static class node {
int x;
int y;
int op;//正反状态
public node(int x, int y, int op) {
this.x = x;
this.y = y;
this.op = op;
}
}
static int n, m, x, y;
static char[][] map;
static boolean[][] road = new boolean[n + 1][m + 1];
static void solve() {
n = sc.nextInt();
m = sc.nextInt();
x = sc.nextInt();
y = sc.nextInt();
map = new char[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
String s=sc.next();
for (int j=1;j<=m;j++){
map[i][j]=s.charAt(j-1);
}
}
Queue<node> q = new LinkedList<>();
int[][] dis = new int[n + 1][m + 1];//到传送门的最短距离
for (int[] di : dis) {
Arrays.fill(di, -1);
}
//将传送门加入队列
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (map[i][j] == '@') {
q.add(new node(i, j, 0));
dis[i][j] = 0;
}
}
}
//从传送门开始bfs计算所有点到门的距离
while (!q.isEmpty()) {
node node = q.poll();
int x = node.x;
int y = node.y;
for (int i = 0; i < 4; i++) {
int xx = x + dx1[i];
int yy = y + dy1[i];
if (xx < 1 || xx > n || yy < 1 || yy > m || map[xx][yy] == '#' || dis[xx][yy] != -1) continue;
dis[xx][yy] = dis[x][y] + 1;
q.add(new node(xx, yy, 0));
}
}
Queue<node> p = new LinkedList<>();
int[][][] vis = new int[n + 1][m + 1][2];
for (int[][] row : vis)
for (int[] col : row) Arrays.fill(col, -1);
p.add(new node(x, y, 0)); // 0 代表正常状态
p.add(new node(x, y, 1)); // 1 代表反转状态
vis[x][y][0] = 0;
vis[x][y][1] = 0;
int ans = Integer.MAX_VALUE;
boolean flag = true;
//使用双端bfs搜索
while (!p.isEmpty()) {
node node1 = p.poll();
node node2 = p.poll();
int x1 = node1.x;
int y1 = node1.y;
int op1 = node1.op;
int x2 = node2.x;
int y2 = node2.y;
int op2 = node2.op;
//检查当前位置
if (map[x1][y1] == '@') {
ans = Math.min(ans, vis[x1][y1][0] + dis[x2][y2]);
flag = false;
}
if (map[x2][y2] == '@') {
ans = Math.min(ans, vis[x2][y2][1] + dis[x1][y1]);
flag = false;
}
//更新队列
for (int i = 0; i < 4; i++) {
int xx1 = x1 + dx1[i];
int yy1 = y1 + dy1[i];
if (xx1 < 1 || xx1 > n || yy1 < 1 || yy1 > m || map[xx1][yy1] == '#' || vis[xx1][yy1][op1] != -1)
continue;
int xx2 = x2 + dx2[i];
int yy2 = y2 + dy2[i];
if (xx2 < 1 || xx2 > n || yy2 < 1 || yy2 > m || map[xx2][yy2] == '#' || vis[xx2][yy2][op2] != -1)
continue;
vis[xx1][yy1][op1]=vis[x1][y1][op1]+1;
vis[xx2][yy2][op2]=vis[x2][y2][op2]+1;
p.add(new node(xx1,yy1,op1));
p.add(new node(xx2,yy2,op2));
}
}
if (flag) {
out.println(-1);
} else {
out.println(ans);
}
}
public static void main(String[] args) {
int T = 1;
while (T-- > 0) {
solve();
}
out.flush();
out.close();
}
//输入输出模版
static Kattio sc = new Kattio();
static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static class Kattio {
static BufferedReader r;
static StringTokenizer st;
public Kattio() {
r = new BufferedReader(new InputStreamReader(System.in));
}
public String next() {
try {
while (st == null || !st.hasMoreTokens()) {
st = new StringTokenizer(r.readLine());
}
return st.nextToken();
} catch (Exception e) {
return null;
}
}
public int nextInt() {
char[] str = next().toCharArray();
int i = 0;
boolean neg = false;
if (str[0] == '-') {
i = 1;
neg = true;
}
int ans = 0;
for (; i < str.length; i++) ans = ans * 10 + (str[i] - '0');
return neg ? -ans : ans;
}
public long nextLong() {
char[] str = next().toCharArray();
int i = 0;
boolean neg = false;
if (str[0] == '-') {
i = 1;
neg = true;
}
long ans = 0;
for (; i < str.length; i++) ans = ans * 10 + (str[i] - '0');
return neg ? -ans : ans;
}
public double nextDouble() {
return Double.parseDouble(next());
}
}
}