牛客小白月赛100--acm的AC题(java题解)

139 阅读4分钟

前言:作者因为懒,第一次看到题目想用简单的方法解决,在比赛过程中没想出来,只能用暴力搜索了

题目描述

众所周知,出题人没玩过双人成行,所以出了这道题

你一觉醒来,发现你和另一个时空的你被困在 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());
        }
    }
}