Leetcode 699. Falling Squares

Leetcode 699. Falling Squares

很久没有写题解了,主要是leetcode没啥有意思的题目,写来没啥意思。

今天这题还算有点意思,可以分析分析。

描述

给你一个序列表示下落的俄罗斯方块的情况。序列里面含有 N 对值 ( L , S ) ,其中 L 表示方块下落的左坐标, S 表示下落方块的边长,这里下落的方块永远是正方形。其中 N 1000 , L 10 8 , S 10 6

问,每下落一次方块,最高的那一列高度是多少。

竞赛想法

这种问题可以归结为,区间更新,区间查询。

如果是之前有一定竞赛基础的人就明显能感觉出来,这种东西要用线段树做。但是一看数据范围: L 10 8 。这就意味着至少需要 2 × 10 8 的节点才能存下一棵线段树,会超过内存。

所以需要离散化,或者一些其他的写法进行优化。姿势有挺多的,我提供两种。

照着这样写就能够得出本问题的最终解法。我没仔细去想这最终解法理论性能上是不是最优的,但是那个算法击败了100%的程序。

class Solution {
    private class SegmentTree{
        int[] tree;
        int[] lazy;
        int N;

        public SegmentTree(int n){
            this.N = n;
            int x = (int)(Math.ceil(Math.log(n) / Math.log(2)));
            int maxSize = 2 * (int)Math.pow(2, x) - 1;
            tree = new int[maxSize];
            lazy = new int[maxSize];
        }

        public int query(int L, int R){
            return queryUtil(0, N - 1, L, R, 0);
        }

        public int queryUtil(int ss, int se, int L, int R, int index){
            if(lazy[index] != 0){
                int update = lazy[index];
                lazy[index] = 0;
                tree[index] = Math.max(tree[index], update);
                if(ss != se){
                    lazy[index * 2 + 1] = Math.max(lazy[index * 2 + 1], update);
                    lazy[index * 2 + 2] = Math.max(lazy[index * 2 + 2], update);
                }
            }
            if(ss > se || L > se || R < ss)  return 0;
            if(L <= ss && R >= se)    return tree[index];

            int mid = ss + (se - ss) / 2;
            return Math.max(queryUtil(ss, mid, L, R, 2 * index + 1), queryUtil(mid + 1, se, L, R, 2 * index + 2));            
        }

        public void update(int L, int R, int height){
            updateValueUtil(0, N - 1, L, R, height, 0);
        }

        public void updateValueUtil(int ss, int se, int L, int R, int height, int index){
            if(lazy[index] != 0){
                int update = lazy[index];
                lazy[index] = 0;
                tree[index] = Math.max(tree[index], update);
                if(ss != se){
                    lazy[index * 2 + 1] = Math.max(lazy[index * 2 + 1], update);
                    lazy[index * 2 + 2] = Math.max(lazy[index * 2 + 2], update);
                }
            }

            if(ss > se || L > se || R < ss)  return;
            if(ss >= L && se <= R){
                tree[index] = Math.max(tree[index], height);
                if(ss != se){
                    lazy[index * 2 + 1] = Math.max(lazy[index * 2 + 1], height);
                    lazy[index * 2 + 2] = Math.max(lazy[index * 2 + 2], height);
                }
                return;
            }

            tree[index] = Math.max(height, tree[index]);
            int mid = ss + (se - ss) / 2;
            updateValueUtil(ss, mid, L, R, height, 2 * index + 1);
            updateValueUtil(mid + 1, se, L, R, height, 2 * index + 2);
        }
    }
    public List<Integer> fallingSquares(int[][] positions) {
            Map<Integer, Integer> map = coordsCompression(positions);

            SegmentTree segmentTree = new SegmentTree(map.size());
            int best = 0;
            List<Integer> res = new ArrayList();

            for(int[] cell : positions){
                int L = map.get(cell[0]);
                int R = map.get(cell[0] + cell[1] - 1);
                int height = segmentTree.query(L, R) + cell[1];
                best = Math.max(best, height);
                res.add(best);
                segmentTree.update(L, R, height);
            }

            return res;
    }
    /*
    离散化
    */
    public Map<Integer, Integer> coordsCompression(int[][] positions){
            Set<Integer> coords = new HashSet();

            for(int[] cell : positions){
                coords.add(cell[0]);
                coords.add(cell[0] + cell[1] - 1);
            }

            List<Integer>sortedCoords = new ArrayList(coords);
            Collections.sort(sortedCoords);

            Map<Integer, Integer> map = new HashMap();
            int t = 0;

            for(int index : sortedCoords)   map.put(index, t++);

            return map;
    }
}
class Solution {
    class Node {
        public int l, r;
        public int max, value;
        public Node left, right;


        public Node(int l, int r, int max, int val) {
            this.l = l;
            this.r = r;
            this.max = max;
            this.value = val;
            this.right = null;
            this.left = null;
        }
    }

    private boolean intersect(Node n, int l, int r) {
        if (r <= n.l || l >= n.r) {
            return false;
        }
        return true;
    }

    private Node insert(Node root, int l, int r, int val) {
        if(root == null)
        {
            return new Node(l, r, r, val);
        }
        if(l <= root.l)
        {
            root.left = insert(root.left, l, r, val);
        }
        else
        {
            root.right = insert(root.right, l, r, val);
        }
        root.max = Math.max(r, root.max);
        return root;
    }

    private int query(Node root, int l, int r) {
        if(root == null || l >= root.max) {
            return 0;
        }

        int ans = 0;
        if(intersect(root, l, r)) {
            ans = root.value;
        }

        ans = Math.max(ans, query(root.left, l, r));

        if(r > root.l) {
            ans = Math.max(ans, query(root.right, l, r));
        }

        return ans;
    }

    public List<Integer> fallingSquares(int[][] positions) {
        List<Integer> ans = new ArrayList<>();
        Node root = null;
        int prev = 0;

        for (int i = 0; i < positions.length; i++) {
            int l = positions[i][0];
            int r = positions[i][0] + positions[i][1];
            int currentHeight = query(root, l, r);
            root = insert(root, l, r, currentHeight + positions[i][1]);
            prev = Math.max(prev, currentHeight + positions[i][1]);
            ans.add(prev);
        }
        return ans;
    }
}

常规想法1

发现 N 非常小,所以从 N 着手考虑。

对于每一次新落下的方块,当前方块的高度等于,所有之前落下的并和它有重叠部分的最高高度,加上当前落下方块的高度。

假设当前处理到了 i ,我们从 i 0 扫描,找到和他有重叠的最高高度,就是这个方块的高度,我们将这个值记录为 h e i g h t [ i ]

扫描二维码关注公众号,回复: 2196607 查看本文章

最终答案显而易见的就是对 h e i g h t 数组做一次取前缀最大值。

时间复杂度为 O ( N 2 )

这个想法其实非常直接,但是这种想法没法优化。

class Solution {
    private class Interval {
        int start, end, height;
        public Interval(int start, int end, int height) {
            this.start = start;
            this.end = end;
            this.height = height;
        }
    }
    public List<Integer> fallingSquares(int[][] positions) {
        List<Interval> intervals = new ArrayList<>();
        List<Integer> res = new ArrayList<>();
        int h = 0;
        for (int[] pos : positions) {
            Interval cur = new Interval(pos[0], pos[0] + pos[1] - 1, pos[1]);
            h = Math.max(h, getHeight(intervals, cur));
            res.add(h);
        }
        return res;
    }
    private int getHeight(List<Interval> intervals, Interval cur) {
        int preMaxHeight = 0;
        for (Interval i : intervals) {
            if (i.end < cur.start) continue;
            if (i.start > cur.end) continue;
            preMaxHeight = Math.max(preMaxHeight, i.height);
        }
        cur.height += preMaxHeight;
        intervals.add(cur);
        return cur.height;
    }
}

常规想法2

多考虑一下就会发现,这种重叠部分可以被当做一个个区间。一个区间内的高度是一致的,所以我们只需要去维护一个个区间就能知道当前方块的高度。

这里可以估算一下区间的个数,不超过 2 × N ,所以也可以用上面那种 O ( N 2 ) 的方式去解决。

假设处理到了 i ,我们扫描区间数列找到所有重合的区间,除开头尾可能没有完全覆盖的区间要进行拆分,其他的区间都要更新为重合区间中的最大值加上 S [ i ] 的数。(关于这儿对于完全覆盖区间的处理,可以选择删除,也可以选择留着然后仍然维护,区别不大)

常规想法3

上面的想法是扫描区间数列得到重合的区间,如果维护区间数列的时候,是按照顺序来的排列的,则可以进行两次二分查找,找到上下界即可。

import java.util.SortedMap;
class Solution {
    public List<Integer> fallingSquares(int[][] positions) {
        List<Integer> res = new ArrayList<>();
        TreeMap<Integer, Integer> startHeight = new TreeMap<>();
        startHeight.put(0, 0); 
        int max = 0;
        for (int[] pos : positions) {
            int start = pos[0], end = start + pos[1];
            Integer from = startHeight.floorKey(start);
            int height = startHeight.subMap(from, end).values().stream().max(Integer::compare).get() + pos[1];
            max = Math.max(height, max);
            res.add(max);
            // remove interval within [start, end)
            int lastHeight = startHeight.floorEntry(end).getValue();
            startHeight.put(start, height);
            startHeight.put(end, lastHeight);
            startHeight.keySet().removeAll(new HashSet<>(startHeight.subMap(start, false, end, false).keySet()));
        }
        return res;
    }
}

猜你喜欢

转载自blog.csdn.net/u013007900/article/details/81065441