699-Falling Squares

Description:
On an infinite number line (x-axis), we drop given squares in the order they are given.

The i-th square dropped (positions[i] = (left, side_length)) is a square with the left-most point being positions[i][0] and sidelength positions[i][1].

The square is dropped with the bottom edge parallel to the number line, and from a higher height than all currently landed squares. We wait for each square to stick before dropping the next.

The squares are infinitely sticky on their bottom edge, and will remain fixed to any positive length surface they touch (either the number line or another square). Squares dropped adjacent to each other will not stick together prematurely.

Return a list ans of heights. Each height ans[i] represents the current highest height of any square we have dropped, after dropping squares represented by positions[0], positions[1], …, positions[i].

Example 1:

Input: [[1, 2], [2, 3], [6, 1]]
Output: [2, 5, 5]
Explanation:

After the first drop of positions[0] = [1, 2]:
_aa
_aa
-------
The maximum height of any square is 2.


After the second drop of positions[1] = [2, 3]:
__aaa
__aaa
__aaa
_aa__
_aa__
--------------
The maximum height of any square is 5.  
The larger square stays on top of the smaller square despite where its center
of gravity is, because squares are infinitely sticky on their bottom edge.


After the third drop of positions[1] = [6, 1]:
__aaa
__aaa
__aaa
_aa
_aa___a
--------------
The maximum height of any square is still 5.

Thus, we return an answer of [2, 5, 5].

Example 2:

Input: [[100, 100], [200, 100]]
Output: [100, 100]
Explanation: Adjacent squares don't get stuck prematurely - only their bottom edge can stick to surfaces.

Note:

1 <= positions.length <= 1000.
1 <= positions[i][0] <= 10^8.
1 <= positions[i][1] <= 10^6.

问题描述:
在一个无限延伸的x轴上我们按顺序投正方形,第i个正方形的左下角的坐标为positions[i][0], 边长为positions[i][1].需要注意的地方有两点:
1.投下来的方式是从一个比所有已经’着陆’的正方形的高度还高的地方投,且正方形底边与x轴平行。并且我们会等待该正方形’着陆’再进行投掷
2.正方形粘性无穷强。降落的过程中要么黏在了x轴上,要么粘在其他正方形上面,另外正方形不会和其他正方形提前黏上(意思就是如果一个正方形的右下角坐标等于该正方形左下角坐标,它们不会黏上)
返回一串高度,每个高度代表之前投的所有正方形中着陆后的最高的高度。

解法1:

/*
思路很直观,针对第i个投下的方块而言,最高的高度只与之前的最高的高度和投下这个方块后可能
达到的更高的高度有关。因此只需要遍历之前的方块,找出与它相交的方块中高度最高的,然后加上该方块
本身的高度与之前的最高的高度对比就行了。
*/
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) {
        //维护第i次之前投的方块
        List<Interval> intervals = new ArrayList<>();
        List<Integer> res = new ArrayList<>();
        int h = 0;
        for (int[] pos : positions) {
            //注意这里,是pos[0] + pos[1] - 1.由于第二条性质
            //正方形不会提前相粘
            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) {
            // Interval i does not intersect with cur
            if (i.end < cur.start) continue;
            if (i.start > cur.end) continue;
            // find the max height beneath cur
            preMaxHeight = Math.max(preMaxHeight, i.height);
        }
        //注意这里,当前投的方块的高度更新了
        cur.height += preMaxHeight;
        //将其放入intervals
        intervals.add(cur);
        return cur.height;
    }
}

解法2:

/*
与之前的解法思路是一样的。关键就是第i个方块的高度是由前i-1个方块的高度影响的,从中找出
最大值,加上自己的边长,然后与当前最大值比较
*/
class Solution {
    public List<Integer> fallingSquares(int[][] positions) {
        int[] qans = new int[positions.length];
        for (int i = 0; i < positions.length; i++) {
            int left = positions[i][0];
            int size = positions[i][1];
            int right = left + size;
            qans[i] += size;

            for (int j = i + 1; j < positions.length; j++) {
                int left2 = positions[j][0];
                int size2 = positions[j][1];
                int right2 = left2 + size2;
                if (left2 < right && left < right2) { //intersect
                    qans[j] = Math.max(qans[j], qans[i]);
                }
            }
        }

        List<Integer> ans = new ArrayList();
        int cur = -1;
        for (int x: qans) {
            cur = Math.max(cur, x);
            ans.add(cur);
        }
        return ans;
    }
}

解法3:

/*
基于坐标变换。举个例子:
假设positions为:[[1, 2], [2, 3], [6, 1]]
坐标变换后得到的坐标为:1, 2, 4, 6
map中存储的其对应的下标为:{1:0, 2:1, 4:2, 6:3}
第一轮:
投入[1, 2]时,经过坐标转换得到L = 0, R = 1
于是从height中找这个区间的最大值,得到0, 加上该方块边长,得到2
然后更新height[0, 1]中的最大值,即height = [2, 2, 0, 0]
当前最大值为2
第二轮:
投入[2, 3],得到:L = 1, R = 2
从height中获取最大值,得到2,加上3,得到5
对height进行更新,height = [2, 5, 5, 0]
当前最大值为5
最后一轮:
投入[6, 1],得到:L = 3, R = 3
从height中获取最大值,得到0,加上1,得到1
对height进行更新,height = [2, 5, 5, 1]
当前最大值为5
于是最大值序列为[2, 5, 5]
这个方法最需要注意的就是:
R = map.get(positions[i][0] + positions[i][1] - 1),这里一定要-1,还是由于规则2
*/
class Solution {
    int[] height;

    public List<Integer> fallingSquares(int[][] positions) {
        Map<Integer, Integer> index = coordsCompression(positions);

        int best = 0;
        List<Integer> res = new ArrayList();

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

        return res;
    }
    public int query(int L, int R){
        int ans = 0;

        for(int i = L;i <= R;i++)   ans = Math.max(ans, height[i]);

        return ans;
    }
    public void update(int L, int R, int h){
        for(int i = L;i <= R;i++)   height[i] = Math.max(h, height[i]);   
    }
    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> index = new HashMap();
        int t = 0;
        for(int coord : sortedCoords)   index.put(coord, t++);
        height = new int[t];

        return index;
    }
}

解法4:

/*
基本思想还是不变,第i个方块需要从前i-1个方块中找出相交的最高点进行更新
维护TreeMap时需要注意几点:
1.一开始要放入(0, 0),因为若没有这个,调用floorKey()时可能返回空(最开始必定为空)
2.对end的维护
3.对start和end更新后,需要将之前的(start, end)(注意双开)的点删除掉
*/
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;
    }
}

解法5(区间树,sgementTree):

/*
如果对区间树不了解的话,可以看看下面这个链接:
https://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/
*/
class Solution {
    private class SegmentTree{
        int[] tree;
        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];
        }
        /*
        L   查询区间的开始
        R   查询区间的结束
        */
        public int query(int L, int R){
            return queryUtil(0, N - 1, L, R, 0);
        }
        /*
        ss  由当前节点代表的区间的开始
        se  由当前节点代表的区间的结束
        L   查询区间的开始
        R   查询区间的结束
        index 区间树的当前节点的下标。由于root下标为0,因此一开始总是传0
        */
        public int queryUtil(int ss, int se, int L, int R, int index){
            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));            
        }
        /*
        L   更新区间的开始
        R   更新区间的结束
        height 用来更新区间的高度
        */
        public void update(int L, int R, int height){
            updateValueUtil(0, N - 1, L, R, height, 0);
        }
         /*
        ss  由当前节点代表的区间的开始
        se  由当前节点代表的区间的结束
        L   更新区间的开始
        R   更新区间的结束
        height 用来更新区间的高度
        index 区间树的当前节点的下标。由于root下标为0,因此一开始总是传0
        */
        public void updateValueUtil(int ss, int se, int L, int R, int height, int index){
            if(ss > se || L > se || R < ss)  return;

            tree[index] = Math.max(height, tree[index]);
            if(ss != se){
                 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;
    }
}

解法6(区间树懒加载):

/*
如果对区间树懒加载不熟悉的话,可以看看这面这个链接:
https://www.geeksforgeeks.org/lazy-propagation-in-segment-tree/
*/
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];
        }
        /*
        L   查询区间的开始
        R   查询区间的结束
        */
        public int query(int L, int R){
            return queryUtil(0, N - 1, L, R, 0);
        }
        /*
        ss  由当前节点代表的区间的开始
        se  由当前节点代表的区间的结束
        L   查询区间的开始
        R   查询区间的结束
        index 区间树的当前节点的下标。由于root下标为0,因此一开始总是传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));            
        }
        /*
        L   更新区间的开始
        R   更新区间的结束
        height 用来更新区间的高度
        */
        public void update(int L, int R, int height){
            updateValueUtil(0, N - 1, L, R, height, 0);
        }
         /*
        ss  由当前节点代表的区间的开始
        se  由当前节点代表的区间的结束
        L   更新区间的开始
        R   更新区间的结束
        height 用来更新区间的高度
        index 区间树的当前节点的下标。由于root下标为0,因此一开始总是传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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值