• Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解


    Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解

    题目地址:https://codingcompetitions.withgoogle.com/kickstart/round/0000000000050ff2/0000000000150aac

    四个解法:

    1. 暴力模拟
    2. 使用HashMap优化,理论时间复杂度最小(好像也是并查集)
    3. (推荐)使用BitSet,实际占用空间特别小,仅仅是 2mn 个比特大小
    4. 使用HashMap实现的并查集方法,在东南西北4个方向上用并查集处理

    解法1:暴力模拟

    下面的代码是我写的暴力模拟办法,只能过样例,提交上去会 Runtime Error.

    import java.util.*;
    import java.io.*;
    public class Solution {
        static String input = "3" + "
    "
            + "5 3 6 2 3" + "
    "
            + "EEWNS" + "
    "
            + "4 3 3 1 1" + "
    "
            + "SESE" + "
    "
            + "11 5 8 3 4" + "
    "
            + "NEESSWWNESE" + "
    ";
        
        public static void main(String[] args) {
            //Scanner sc = new Scanner(System.in);
            Scanner sc = new Scanner(new StringReader(input));
            int T = sc.nextInt();
            int[][] D = new int[128][2];
            D['E'] = new int[]{0, 1};
            D['W'] = new int[]{0, -1};
            D['N'] = new int[]{-1,0};
            D['S'] = new int[]{1,0};
            for (int i = 0; i < T; i++) {
                int N = sc.nextInt();
                int R = sc.nextInt();
                int C = sc.nextInt();
                int Sr = sc.nextInt();
                int Sc = sc.nextInt();
                sc.nextLine();
                String seq = sc.nextLine();
                boolean[][] map = new boolean[R + 1][C + 1];
                map[Sr][Sc] = true;
                
                for (int j = 0; j < N; j++) {
                    do {
                        Sr += D[seq.charAt(j)][0];
                        Sc += D[seq.charAt(j)][1];
                    } while (map[Sr][Sc] == true);
                    map[Sr][Sc] = true;
                }
                System.out.println("Case #" + (i + 1) + ": " + Sr + " " + Sc);
            }
        }
    }
    

    解法2:HashMap优化

    下面是使用HashMap能过的版本,原答案来自StackExchange上的Python解法.

    注意静态内部类 Point,我重写了对象的equals()方法和hashCode()方法,以便 HashMap 能正确查找对象。hashCode()方法用于计算哈希值,不重写的会采用对象内存地址作为哈希值,这是我们不希望看到的。equals()方法用于解决哈希冲突后的对象实际内容比对。

    import java.util.*;
    import java.io.*;
    public class Solution {
    
        static class Point {
            int x;
            int y;
            Point() {}
            Point(int xx, int yy) { x = xx; y = yy; }
            @Override
            public boolean equals(Object obj) {
                Point that = (Point) obj;
                return this.x == that.x && this.y == that.y;
            }
            @Override
            public int hashCode() {
                return x * 50000  + y;
            }
        }
        static HashMap<Point, Point>[] neighbors;
    
        public static void init() {
            neighbors = new HashMap[128];
            neighbors['W'] = new HashMap<Point, Point>();
            neighbors['E'] = new HashMap<Point, Point>();
            neighbors['S'] = new HashMap<Point, Point>();
            neighbors['N'] = new HashMap<Point, Point>();
        }
    
        public static Point getNeighbor(Point cur, char direction) {
            if (neighbors[direction].containsKey(cur)) {
                return neighbors[direction].get(cur);
            }
            switch(direction) {
                case 'W': return new Point(cur.x - 1, cur.y);
                case 'E': return new Point(cur.x + 1, cur.y);
                case 'N': return new Point(cur.x, cur.y - 1);
                case 'S': return new Point(cur.x, cur.y + 1);
                default: return null;
            }
        }
    
        public static void linkNeighbors(Point cur) {
            Point west = getNeighbor(cur, 'W');
            Point east = getNeighbor(cur, 'E');
            Point north = getNeighbor(cur, 'N');
            Point south = getNeighbor(cur, 'S');
            neighbors['W'].put(east, west);
            neighbors['E'].put(west, east);
            neighbors['N'].put(south, north);
            neighbors['S'].put(north, south);
        }
        
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int T = sc.nextInt();
            for (int i = 0; i < T; i++) {
                int N = sc.nextInt();
                int R = sc.nextInt();
                int C = sc.nextInt();
                int Sr = sc.nextInt();
                int Sc = sc.nextInt();
                sc.nextLine();  // skip 
     at end of previous line
                String seq = sc.nextLine();
                
                init();
                Point cur = new Point(Sc, Sr);
                for (int j = 0; j < N; j++) {
                    linkNeighbors(cur);
                    cur = getNeighbor(cur, seq.charAt(j));
                }
    
                System.out.println("Case #" + (i + 1) + ": " + cur.y + " " + cur.x);
            }
        }
    }
    

    解法3: BitSet

    思路来源: http://codeforces.com/blog/entry/67224?#comment-513594

    核心就是利用 BitSet 提供的 previousSetBit()nextSetBit() 方法,虽然这两个方法理论上是 O(n) 时间复杂度,但由于是位操作,且Java肯定做了某些特殊优化,所有不仅占用内存特别小,运行速度也快。

    import java.util.*;
    import java.io.*;
    public class Solution {
        
        static BitSet[] rowBitsets;   // m horizontal bitset
        static BitSet[] colBitsets;   // n vertical bitsets
    
        public static void init(int rows, int cols) {
            // initilize m + n bitsets
            rowBitsets = new BitSet[rows + 1];
            colBitsets = new BitSet[cols + 1];
            for (int i = 1; i <= rows; i++) rowBitsets[i] = new BitSet(cols + 1);
            for (int i = 1; i <= cols; i++) colBitsets[i] = new BitSet(rows + 1);
        }
    
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int T = sc.nextInt();
            for (int i = 0; i < T; i++) {
                int N = sc.nextInt();
                int R = sc.nextInt();
                int C = sc.nextInt();
                int Sr = sc.nextInt();
                int Sc = sc.nextInt();
                sc.nextLine();  // skip 
     at end of previous line
                String seq = sc.nextLine();
                
                init(R, C);
                for (int j = 0; j < N; j++) {
                    rowBitsets[Sr].set(Sc);
                    colBitsets[Sc].set(Sr);
    
                    switch(seq.charAt(j)) {
                        case 'W': Sc = rowBitsets[Sr].previousClearBit(Sc); break;
                        case 'E': Sc = rowBitsets[Sr].nextClearBit(Sc); break;
                        case 'N': Sr = colBitsets[Sc].previousClearBit(Sr); break;
                        case 'S': Sr = colBitsets[Sc].nextClearBit(Sr); break;
                        default: break;
                    }
                }
    
                System.out.println("Case #" + (i + 1) + ": " + Sr + " " + Sc);
            }
        }
    }
    

    解法4:使用并查集

    import java.util.*;
    import java.io.*;
    public class Solution {
        
        static class Point {
            int x, y;
            Point(int xx, int yy) { x = xx; y = yy; }
            @Override
            public boolean equals(Object obj) {
                Point that = (Point) obj;
                return this.x == that.x && this.y == that.y;
            }
            @Override
            public int hashCode() {
                return x * 50001 + y;
            }
        }
    
        public static Point findNeighbor(HashMap<Point, Point> map, Point cur) {
            // 向某个方向查找
            if (!map.containsKey(cur) || map.get(cur).equals(cur)) {
                // 当前结点cur在目标方向上没有已知的邻居,返回当前结点
                return cur;
            } else {
                // 当前结点cur在目标方向上有个邻居,在目标方向上查找邻居的邻居,直到找到最远的邻居
                // 并将当前结点在目标方向上的邻居更新为最远邻居,返回最远邻居
                Point res = findNeighbor(map, map.get(cur));
                map.put(cur, res);
                return res;
            }
        }
    
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int T = sc.nextInt();
            for (int i = 0; i < T; i++) {
                int N = sc.nextInt();
                int R = sc.nextInt();
                int C = sc.nextInt();
                int Sr = sc.nextInt();
                int Sc = sc.nextInt();
                sc.nextLine();  // skip 
     at end of previous line
                String seq = sc.nextLine();
                
                HashMap<Point, Point> mapN = new HashMap<>();
                HashMap<Point, Point> mapS = new HashMap<>();
                HashMap<Point, Point> mapW = new HashMap<>();
                HashMap<Point, Point> mapE = new HashMap<>();
                Point cur = new Point(Sr, Sc);
                for (int j = 0; j < N; j++) {
                    // 实在是搞不懂这句
                    mapN.put(new Point(cur.x + 1, cur.y), cur);
                    mapS.put(new Point(cur.x - 1, cur.y), cur);
                    mapW.put(new Point(cur.x, cur.y + 1), cur);
                    mapE.put(new Point(cur.x, cur.y - 1), cur);
                    // 更新4个点
                    switch(seq.charAt(j)) {
                        case 'N':
                            Point t1 = findNeighbor(mapN, cur); // 找到当前结点的最远邻居
                            cur = new Point(t1.x - 1, t1.y);
                            break;
                        case 'S':
                            Point t2 = findNeighbor(mapS, cur); // 找到当前结点的最远邻居
                            cur = new Point(t2.x + 1, t2.y);
                            break;
                        case 'E':  
                            Point t3 = findNeighbor(mapE, cur); // 找到当前结点的最远邻居
                            cur = new Point(t3.x, t3.y + 1);
                            break;
                        case 'W':
                            Point t4 = findNeighbor(mapW, cur); // 找到当前结点的最远邻居
                            cur = new Point(t4.x, t4.y - 1);
                            break;
                        default: break;
                    }
                    System.out.println(seq.charAt(j) + " " + cur.x + " " + cur.y);
                }
    
                System.out.println("Case #" + (i + 1) + ": " + cur.x + " " + cur.y);
            }
        }
    }
    
  • 相关阅读:
    洛谷 1.5.1 Number Triangles 数字金字塔
    洛谷 Sorting a Three-Valued Sequence 三值的排序
    洛谷 Transformations 方块转换
    POJ 1401 Factorial
    Java面试那些事
    JVM字节码执行引擎
    一个工作三年左右的Java程序员和大家谈谈从业心得
    浅谈volatile关键字
    Java内存模型
    Integer 错误的加锁
  • 原文地址:https://www.cnblogs.com/fondoger/p/google-kick-start-2019-round-C-question-1-wiggle-walk-solution.html
Copyright © 2020-2023  润新知