Leetcode 699. Falling Squares

我就是我 2022-05-20 09:08 264阅读 0赞

Leetcode 699. Falling Squares

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

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

描述

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

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

竞赛想法

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

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

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

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

  1. class Solution {
  2. private class SegmentTree{
  3. int[] tree;
  4. int[] lazy;
  5. int N;
  6. public SegmentTree(int n){
  7. this.N = n;
  8. int x = (int)(Math.ceil(Math.log(n) / Math.log(2)));
  9. int maxSize = 2 * (int)Math.pow(2, x) - 1;
  10. tree = new int[maxSize];
  11. lazy = new int[maxSize];
  12. }
  13. public int query(int L, int R){
  14. return queryUtil(0, N - 1, L, R, 0);
  15. }
  16. public int queryUtil(int ss, int se, int L, int R, int index){
  17. if(lazy[index] != 0){
  18. int update = lazy[index];
  19. lazy[index] = 0;
  20. tree[index] = Math.max(tree[index], update);
  21. if(ss != se){
  22. lazy[index * 2 + 1] = Math.max(lazy[index * 2 + 1], update);
  23. lazy[index * 2 + 2] = Math.max(lazy[index * 2 + 2], update);
  24. }
  25. }
  26. if(ss > se || L > se || R < ss) return 0;
  27. if(L <= ss && R >= se) return tree[index];
  28. int mid = ss + (se - ss) / 2;
  29. return Math.max(queryUtil(ss, mid, L, R, 2 * index + 1), queryUtil(mid + 1, se, L, R, 2 * index + 2));
  30. }
  31. public void update(int L, int R, int height){
  32. updateValueUtil(0, N - 1, L, R, height, 0);
  33. }
  34. public void updateValueUtil(int ss, int se, int L, int R, int height, int index){
  35. if(lazy[index] != 0){
  36. int update = lazy[index];
  37. lazy[index] = 0;
  38. tree[index] = Math.max(tree[index], update);
  39. if(ss != se){
  40. lazy[index * 2 + 1] = Math.max(lazy[index * 2 + 1], update);
  41. lazy[index * 2 + 2] = Math.max(lazy[index * 2 + 2], update);
  42. }
  43. }
  44. if(ss > se || L > se || R < ss) return;
  45. if(ss >= L && se <= R){
  46. tree[index] = Math.max(tree[index], height);
  47. if(ss != se){
  48. lazy[index * 2 + 1] = Math.max(lazy[index * 2 + 1], height);
  49. lazy[index * 2 + 2] = Math.max(lazy[index * 2 + 2], height);
  50. }
  51. return;
  52. }
  53. tree[index] = Math.max(height, tree[index]);
  54. int mid = ss + (se - ss) / 2;
  55. updateValueUtil(ss, mid, L, R, height, 2 * index + 1);
  56. updateValueUtil(mid + 1, se, L, R, height, 2 * index + 2);
  57. }
  58. }
  59. public List<Integer> fallingSquares(int[][] positions) {
  60. Map<Integer, Integer> map = coordsCompression(positions);
  61. SegmentTree segmentTree = new SegmentTree(map.size());
  62. int best = 0;
  63. List<Integer> res = new ArrayList();
  64. for(int[] cell : positions){
  65. int L = map.get(cell[0]);
  66. int R = map.get(cell[0] + cell[1] - 1);
  67. int height = segmentTree.query(L, R) + cell[1];
  68. best = Math.max(best, height);
  69. res.add(best);
  70. segmentTree.update(L, R, height);
  71. }
  72. return res;
  73. }
  74. /* 离散化 */
  75. public Map<Integer, Integer> coordsCompression(int[][] positions){
  76. Set<Integer> coords = new HashSet();
  77. for(int[] cell : positions){
  78. coords.add(cell[0]);
  79. coords.add(cell[0] + cell[1] - 1);
  80. }
  81. List<Integer>sortedCoords = new ArrayList(coords);
  82. Collections.sort(sortedCoords);
  83. Map<Integer, Integer> map = new HashMap();
  84. int t = 0;
  85. for(int index : sortedCoords) map.put(index, t++);
  86. return map;
  87. }
  88. }
  89. class Solution {
  90. class Node {
  91. public int l, r;
  92. public int max, value;
  93. public Node left, right;
  94. public Node(int l, int r, int max, int val) {
  95. this.l = l;
  96. this.r = r;
  97. this.max = max;
  98. this.value = val;
  99. this.right = null;
  100. this.left = null;
  101. }
  102. }
  103. private boolean intersect(Node n, int l, int r) {
  104. if (r <= n.l || l >= n.r) {
  105. return false;
  106. }
  107. return true;
  108. }
  109. private Node insert(Node root, int l, int r, int val) {
  110. if(root == null)
  111. {
  112. return new Node(l, r, r, val);
  113. }
  114. if(l <= root.l)
  115. {
  116. root.left = insert(root.left, l, r, val);
  117. }
  118. else
  119. {
  120. root.right = insert(root.right, l, r, val);
  121. }
  122. root.max = Math.max(r, root.max);
  123. return root;
  124. }
  125. private int query(Node root, int l, int r) {
  126. if(root == null || l >= root.max) {
  127. return 0;
  128. }
  129. int ans = 0;
  130. if(intersect(root, l, r)) {
  131. ans = root.value;
  132. }
  133. ans = Math.max(ans, query(root.left, l, r));
  134. if(r > root.l) {
  135. ans = Math.max(ans, query(root.right, l, r));
  136. }
  137. return ans;
  138. }
  139. public List<Integer> fallingSquares(int[][] positions) {
  140. List<Integer> ans = new ArrayList<>();
  141. Node root = null;
  142. int prev = 0;
  143. for (int i = 0; i < positions.length; i++) {
  144. int l = positions[i][0];
  145. int r = positions[i][0] + positions[i][1];
  146. int currentHeight = query(root, l, r);
  147. root = insert(root, l, r, currentHeight + positions[i][1]);
  148. prev = Math.max(prev, currentHeight + positions[i][1]);
  149. ans.add(prev);
  150. }
  151. return ans;
  152. }
  153. }

常规想法1

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

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

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

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

时间复杂度为O(N2) O ( N 2 ) 。

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

  1. class Solution {
  2. private class Interval {
  3. int start, end, height;
  4. public Interval(int start, int end, int height) {
  5. this.start = start;
  6. this.end = end;
  7. this.height = height;
  8. }
  9. }
  10. public List<Integer> fallingSquares(int[][] positions) {
  11. List<Interval> intervals = new ArrayList<>();
  12. List<Integer> res = new ArrayList<>();
  13. int h = 0;
  14. for (int[] pos : positions) {
  15. Interval cur = new Interval(pos[0], pos[0] + pos[1] - 1, pos[1]);
  16. h = Math.max(h, getHeight(intervals, cur));
  17. res.add(h);
  18. }
  19. return res;
  20. }
  21. private int getHeight(List<Interval> intervals, Interval cur) {
  22. int preMaxHeight = 0;
  23. for (Interval i : intervals) {
  24. if (i.end < cur.start) continue;
  25. if (i.start > cur.end) continue;
  26. preMaxHeight = Math.max(preMaxHeight, i.height);
  27. }
  28. cur.height += preMaxHeight;
  29. intervals.add(cur);
  30. return cur.height;
  31. }
  32. }

常规想法2

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

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

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

常规想法3

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

  1. import java.util.SortedMap;
  2. class Solution {
  3. public List<Integer> fallingSquares(int[][] positions) {
  4. List<Integer> res = new ArrayList<>();
  5. TreeMap<Integer, Integer> startHeight = new TreeMap<>();
  6. startHeight.put(0, 0);
  7. int max = 0;
  8. for (int[] pos : positions) {
  9. int start = pos[0], end = start + pos[1];
  10. Integer from = startHeight.floorKey(start);
  11. int height = startHeight.subMap(from, end).values().stream().max(Integer::compare).get() + pos[1];
  12. max = Math.max(height, max);
  13. res.add(max);
  14. // remove interval within [start, end)
  15. int lastHeight = startHeight.floorEntry(end).getValue();
  16. startHeight.put(start, height);
  17. startHeight.put(end, lastHeight);
  18. startHeight.keySet().removeAll(new HashSet<>(startHeight.subMap(start, false, end, false).keySet()));
  19. }
  20. return res;
  21. }
  22. }

发表评论

表情:
评论列表 (有 0 条评论,264人围观)

还没有评论,来说两句吧...

相关阅读