• LeetCode 115th Weekly Contest 总结


    总体来说,这周题目比较难。以后还是每周都来总结下周赛。

    1. 957. Prison Cells After N Days

    题意

    类似于细胞生命的游戏,每天细胞会根据前一天的状态进行演变,演变规则:

    Each day, whether the cell is occupied or vacant changes according to the
    following rules:
    If a cell has two adjacent neighbors that are both occupied or both vacant,
    then the cell becomes occupied.
    Otherwise, it becomes vacant.

    思路

    开始比较直接的做法就是遍历n天,再遍历n个细胞:

    public int[] prisonAfterNDays(int[] cells, int N) {
            while (N > 0) {
                N--;
                int[] cells2 = new int[8];
                for (int i = 1; i < 7; ++i)
                    cells2[i] = cells[i - 1] == cells[i + 1] ? 1 : 0;
                cells = cells2;
            }
            return cells;
    }
    

    但是这个解答在N很大时超时,于是必须考虑优化方案了。

    • 我们来思考一下,这个状态中可能出现重复的吗?也就是如果存在循环,就会好办很多,我们就可以取周期的余,就到了跳过一次循环后的状态了;其实一共8个细胞,出去首尾两个状态始终是0,其余6个组成的状态数一共是2^6=64种,所以变化那么多天,肯定是存在循环的(其实就是也可以猜测如果没有循环这题就没法解了= =)
    • 下一步,怎么判断出现过的状态呢?并且还需要知道是在之前哪天出现的,因为今天减去上一期相同状态出现的那天,就等于循环的周期啦。自然而然想到应该用hash table存储。
    • 还需要注意的点就是,hash table中的key的存储,应该是用字符串化后的数组,不然hash table判断的时候用的是数组的reference,每次都不会一样.

    代码:

    class Solution {
        public int[] prisonAfterNDays(int[] cells, int N) {
            int[] tmp = new int[8];
            Map<String, Integer> map = new HashMap<>();
            while (N > 0) {
                map.put(Arrays.toString(cells), N);
                N--;
                for (int i = 1; i < 7; i++) {
                    tmp[i] = cells[i - 1] == cells[i + 1] ? 1 : 0;
                }
                for (int i = 0; i < 8; i++) cells[i] = tmp[i];
                if (map.containsKey(Arrays.toString(cells))) {
                    int T = map.get(Arrays.toString(cells)) - N;  //周期
                    N %= T;
                }
            }
            return cells;
        }
    }
    

    总结:

    这种状态转换,且n还很大,一般就是需要找循环,然后取余mod,来减少循环长度。
    这样的就遍历数组,再加上一个优化步骤的题目,面试也会将常考

    2. 958. Check Completeness of a Binary Tree

    题意

    判断一棵树是否是完全二叉树

    思路

    根据完全二叉树的性质,最下层节点应该都在左边;

    • 可以考虑下层序遍历,如果是完全二叉树的话,那么当遇到空节点的时候,中后就不应该再遇到空节点了。
    • 层序遍历,无论是否为空节点都add进队列,当队列头是空节点时,说明遇到第一个空节点了;然后再判断队列中剩下的节点,如果有非空的,则不是完全二叉树

    代码:

    class Solution {
        public boolean isCompleteTree(TreeNode root) {
            Queue<TreeNode> queue = new LinkedList<>();
            queue.offer(root);
            while (queue.peek() != null) {
                TreeNode cur = queue.poll();
                queue.add(cur.left);
                queue.add(cur.right);
            }
            while (!queue.isEmpty() && queue.peek() == null) {
                queue.poll();
            }
            return queue.isEmpty();
        }
    }
    

    总结

    树的层序遍历的扩展

    3. 959. Regions Cut By Slashes

    哈哈比赛时这题题目都没看懂 = =

    题意

    In a N x N grid composed of 1 x 1 squares, each 1 x 1 square consists of a /
    , or blank space. These characters divide the square into contiguous
    regions.

    大概就是给一个字符串代表矩形的分割方式,求在矩形区域的个数。

    思路

    什么鬼题目??大脑一片空白?莫急,让我们慢慢分析一下。

    • 怎么才能判断这块区域是一块呢?这种图形题,又不可能考计算几何 = =,所以唯一可能就是通过染色的方法,我们可以标记对吧;
    • 那么怎么染色呢?是不是想到了并查集?就是将相连的区域都染为同一个颜色,最后颜色的树木不就是的区域的数目吗?
    • 有了基本的思路,方法是可行的,到了具体实现了;这里一个难想到的点就是,根据/这两个字符,去分割矩形小框框,怎么去分呢?这里需要把每个小矩形再分成四个小区域:如下图所示:
      这样,我们就可以根据当前是什么符号,去将不同的区域进行连接了

    pig

    代码:

    class Solution {
        class UF {
            int[] parent;
            public UF(int n) {
                parent = new int[n];
                for (int i = 0; i < n; i++) parent[i] = i;
            } 
            public int  find(int x) {
                if (parent[x] != x) parent[x] = find(parent[x]);
                return parent[x];
            }
            public void union(int x, int y) {
                parent[find(x)] = find(y);
            }
        }
        
        int count = 0;
        UF uf;
        public int regionsBySlashes(String[] grid) {
            int n = grid.length;
            uf = new UF(n * n * 4);
            count = n * n * 4;
            //四个小区块:top: 0, right: 1, bottom: 2, left: 3
            for (int r = 0; r < n; r++) {
                for (int  c = 0; c < n; c++) {
                    if (r > 0) {
                        check(changeIndex(n, r-1, c, 2), changeIndex(n, r, c, 0));
                    }
                    if (c > 0) {
                        check(changeIndex(n, r, c - 1, 1), changeIndex(n, r, c, 3));
                    }
                    if (grid[r].charAt(c) != '/') {
                        check(changeIndex(n, r, c, 3), changeIndex(n, r, c, 2));
                        check(changeIndex(n, r, c, 1), changeIndex(n, r, c, 0));
                    }
                    if (grid[r].charAt(c) != '\') {
                        check(changeIndex(n, r, c, 0), changeIndex(n, r, c, 3));
                        check(changeIndex(n, r, c, 1), changeIndex(n, r, c, 2));
                    }
                }
            }
            return count;
        }
        
        private int changeIndex(int n, int x, int y, int k) {
            return (x * n + y) * 4 + k;
        }
        
        private void check(int n1, int n2) {
            if (uf.find(n1) != uf.find(n2)) {
                uf.union(n1, n2);
                count--;
            }
        }
    }
    

    总结

    并查集的考察越来多多了

    4. 960. Delete Columns to Make Sorted III

    题意

    这题是之前几题的延续
    删除某些列后,每行的元素都是按字母排序的

    Suppose we chose a set of deletion indices D such that after deletions, the
    final array has every element (row) in lexicographic order.

    思路

    • 求每个字符串最少删掉的字符,使得每个字符串中的字符都是递增的;我们可以先考虑考虑1个字符串的情况:
      也就是说,比如"edcba",求它最少减少的字符,使得它自增,是不是觉得很熟悉呢?问题可以转化为最长递增子序列LIS,n - LIS
      即为所求了!回忆LIS的基本解法是:
    for j : 0 -> n
      for i : 0 -> j
         if (A[i] < A[j]) dp[j] = max(dp[j], dp[i] + 1)
    
    • 想清楚了一个字符串的case,再来想想多个字符串怎么办?
      这里的一个思路就是 将每列看成一个元素,那么只要判断在每个case下,是不是每行元素都满足递增,如果满足,则更新dp;否则跳过;

    代码:

    class Solution {
        public int minDeletionSize(String[] A) {
            int m = A.length, n = A[0].length();
            int[] dp = new int[n];  //每列看成一个元素!
            int res = Integer.MAX_VALUE;
            Arrays.fill(dp, 1);
            for (int j = 0; j < n; j++) {
                for (int i = 0; i < j; i++) {
                    boolean valid = true;
                    //检查每个字符串是不是都满足
                    for (int r = 0; r < m; r++) {
                        if (A[r].charAt(i) > A[r].charAt(j)) {
                            valid = false;
                            break;
                        }
                    }
                    if (valid) dp[j] = Math.max(dp[j], dp[i] + 1);
                }
                res = Math.min(res, n - dp[j]);
            }
            return res;
        }
    }
    

    总结

    其实这题就是LIS的变种

    总体来说,这次周赛除了第三题,其他都可以做出来的,其他的都比较常规,但是...emmm 加油吧

  • 相关阅读:
    图论-桥/割点/双连通分量/缩点/LCA
    未解决的问题
    hdu2586(LCA最近公共祖先)
    LCA最近公共祖先(least common ancestors)
    111
    poj1703 Find them, Catch them 并查集
    关于背包的注意事项
    codeforces343A A. Rational Resistance
    hdu(1171)多重背包
    HTML5事件—visibilitychange 页面可见性改变事件
  • 原文地址:https://www.cnblogs.com/shawshawwan/p/10127767.html
Copyright © 2020-2023  润新知