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.

这道题不就是经典的俄罗斯方块么?!只不过是简化版的,我们只有方块下落,没有其他那些奇形怪状的东西,这样简化了难度,不过方块的大小不是固定的,有可能很大,但是不管方块再大,只要有一点点部分搭在其他方块上面,整个方块都会在上面,并不会掉下来,让我们求每落下一个方块后的最大高度。我们知道返回的是每落下一个方块后当前场景中的最大高度,那么返回的数组的长度就应该和落下方块的个数相同。所以我们可以建立一个heights数组,其中heights[i]表示第i块方块落下后所在的高度,那么第i块方块落下后场景的最大高度就是[0, i]区间内的最大值。那么我们在求出heights数组后,只要不停返回[0, i]区间内的最大值即可。继续来看,这道题的难点就是方块重叠的情况,我们先来想,如果各个方块不重叠,那么heights[i]的高度就是每个方块自身的高度。一旦重叠了,就得在已有的基础上再加上自身的高度。那么我们可以采用brute force的思想,对于每个一个下落的方块,我们都去看和后面将要落下的方块有没有重叠,有的话,和后面将要落下的方块的位置相比较,取二者中较大值为后面要落下的方块位置高度heights[j]。判读两个方块是否重叠的方法是如果方块2的左边界小于方块1的右边界,并且方块2点右边界大于方块1点左边界。就拿题目中的例子1来举例吧,第一个下落的方块的范围是[1, 3],长度为2,则heights[0]=2,然后我们看其和第二个方块[2, 5]是否重叠,发现是重叠的,则heights[1]更新为2,再看第三个方块[6, 7],不重叠,不更新。然后第二个方块落下,此时累加高度,则heights[1]=5,再看第三个方块,不重叠,不更新。然后第三个方块落下, heights[2]=1。此时我们heights数组更新好了,然后我们开始从头遍历,维护一个当前最大值curMax,每次将[0, i]中最大值加入结果res即可,参见代码如下:

解法一:

class Solution {
public:
vector<int> fallingSquares(vector<pair<int, int>>& positions) {
int n = positions.size(), cur = ;
vector<int> heights(n), res;
for (int i = ; i < n; ++i) {
int len = positions[i].second, left = positions[i].first, right = left + len;
heights[i] += len;
for (int j = i + ; j < n; ++j) {
int l = positions[j].first, r = l + positions[j].second;
if (l < right && r > left) {
heights[j] = max(heights[j], heights[i]);
}
}
}
for (int h : heights) {
cur = max(cur, h);
res.push_back(cur);
}
return res;
}
};

我们来看一种时间复杂度为O(nlogn)的解法,这种解法将每一个不同高度的区间都存到了一个HashMap中,然后每当有新的方块落下的时候,可以使用二分法来快速定位到可能发生重叠的区间的位置,如果有重叠的话,将原区间再根据高度拆成多个小区间,并且一直维护一个当前出现过的最高值,并加入结果res中。我们的HashMap的映射是建立pair和int之间的映射,由于HashMap是有自动排序的功能,默认的是使用pair中第一个元素,正好就是每个方块的起始位置。然后我们遍历每个下落的方块,建立一个临时的二维数组t,用来保存拆分后的小区间。然后我们取出当前方块的起始终止位置start和end,然后我们希望在HashMap中找第一个不大于当前方块起始位置的区间,在Java中我们可以使用floorKey()函数,但是在C++中,我们只有lower_bound()和upper_bound()可以用,分别表示找第一个不大于目标值,和第一个大于目标值的区间,那么我们为了找到第一个不大于当前起始位置的区间,可以先用upper_bound()来找到第一个大于起始位置的区间,然后向前移动一个,就是第一个不大于的了。注意向前移动操作有前提条件,就是upper_bound()返回的位置不能是首位置,否则无法前移,还有就是如果前一个区间的结束为止小于等于当前区间的起始位置,说明两个区间没有重叠,我们再移回来。

下面就要进行拆分区间的核心操作了,我们用一个while循环,循环条件是it存在,并且it指向区间的起始位置小于当前区间的结束位置。由于之前的操作确定了这两个区间一定会有重叠,那么重叠的方式就有一下四种(上方为当前区间,下方为it区间):

      ———
| |
———
————————
| |
————————

如上图所示,如果当前区间(上方)的起始位置大于it指向区间(下方)的起始位置,说明红色那段区间需要被拆分出来,我们将其拆分出来并存入数组t中。

  ———
| |
———
————————
| |
————————

如上图所示,如果当前区间(上方)的结束位置小于it指向区间(下方)的结束位置,说明洋红色那段区间需要被拆分出来,我们将其拆分出来并存入数组t中。

  ———
| |
———
————————
| |
————————

如上图所示,红色区间和洋红色区间都需要拆分出来,我们将其拆分出来并存入数组t中。

————————
| |
————————
———
| |
———

如上图所示,底层方块被完全覆盖了,没有区间需要被拆分出来。

我们用底层it指向的区间的高度来更新h,这里的h表示当前方块下落后的基础高度,然后我们将底层it指向的区间删除,因为我们已经将没有被覆盖的区间拆分出来并存入数组t中了。注意erase()函数返回的是被删除区间的下一个位置,这样使得我们的while函数能继续判断,直到it区间和当前区间不再重叠位置。退出while循环后,我们需要将当前下落方块的区间加入HashMap中,高度为基础高度h加上自身高度len。接下来就把数组t中拆分出来的小区间都加入到HashMap中,然后用当前用h+len来更新curMax,表示当前场景最大高度,加入结果res中,参见代码如下:

解法二:

class Solution {
public:
vector<int> fallingSquares(vector<pair<int, int>>& positions) {
vector<int> res;
map<pair<int, int>, int> m;
int curMax = ;
for (auto &pos : positions) {
vector<vector<int>> t;
int len = pos.second, start = pos.first, end = start + len, h = ;
auto it = m.upper_bound({start, start});
if (it != m.begin() && (--it)->first.second <= start) ++it;
while (it != m.end() && it->first.first < end) {
if (start > it->first.first) t.push_back({it->first.first, start, it->second});
if (end < it->first.second) t.push_back({end, it->first.second, it->second});
h = max(h, it->second);
it = m.erase(it);
}
m[{start, end}] = h + len;
for (auto &a : t) m[{a[], a[]}] = a[];
curMax = max(curMax, h + len);
res.push_back(curMax);
}
return res;
}
};

类似题目:

The Skyline Problem

参考资料:

https://leetcode.com/problems/falling-squares/solution/

https://leetcode.com/problems/falling-squares/discuss/108769/C++-O(nlogn)

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Falling Squares 下落的方块的更多相关文章

  1. Falling Squares

    2020-01-08 10:16:37 一.Falling squares 问题描述: 问题求解: 本题其实也是一条经典的区间问题,对于区间问题,往往可以使用map来进行区间的维护操作. class ...

  2. [Swift]LeetCode699. 掉落的方块 | Falling Squares

    On an infinite number line (x-axis), we drop given squares in the order they are given. The i-th squ ...

  3. 【leetcode】699. Falling Squares

    题目如下: On an infinite number line (x-axis), we drop given squares in the order they are given. The i- ...

  4. leetcode 699. Falling Squares 线段树的实现

    线段树实现.很多细节值得品味 都在注释里面了 class SegTree: def __init__(self,N,query_fn,update_fn): self.tree=[0]*(2*N+2) ...

  5. [LeetCode] Word Squares 单词平方

    Given a set of words (without duplicates), find all word squares you can build from them. A sequence ...

  6. Leetcode: Word Squares && Summary: Another Important Implementation of Trie(Retrieve all the words with a given Prefix)

    Given a set of words (without duplicates), find all word squares you can build from them. A sequence ...

  7. LeetCode Perfect Squares

    原题链接在这里:https://leetcode.com/problems/perfect-squares/ 题目: Given a positive integer n, find the leas ...

  8. #Leetcode# 977. Squares of a Sorted Array

    https://leetcode.com/problems/squares-of-a-sorted-array/ Given an array of integers A sorted in non- ...

  9. LeetCode699. Falling Squares

    On an infinite number line (x-axis), we drop given squares in the order they are given. The i-th squ ...

随机推荐

  1. Tram

    Tram 题目大意:给你一个图,这个图上有n点和边.点上有开关,开关开始指向一条道路,拨动开关可以使开关指向由开关出发的任意一条路径,读入,a,b,求,至少要拨动几次才能从a点走到b点. 注释:n&l ...

  2. 四十六、android中的Bitmap

    四十六.android中的Bitmap: http://www.cnblogs.com/linjiqin/archive/2011/12/28/2304940.html 四十七.实现调用Android ...

  3. java1.8版本的HashMap源码剖析

    一.摘要 以下分析内容均是基于JDK1.8产生的,同时也和JDK1.7版本的hashmap做了一些比较.在1.7版本中,HashMap的实现是基于数组+链表的形式,而在1.8版本中则引入了红黑树,但其 ...

  4. java编程思想笔记(1)

    java编程思想笔记(1) 一,对象的创建和生命周期 对象的数据位于何处?怎样控制对象的生命周期? 在堆(heap)的内存池中动态地创建对象. java完全采用了动态内存分配方式. 二,垃圾回收器 自 ...

  5. spring框架学习笔记4:SpringAOP实现原理

    AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.OOP引入 ...

  6. 记录python接口自动化测试--pycharm执行测试用例时需要使用的姿势(解决if __name__ == "__main__":里面的程序不生效的问题)(第三目)

    1.只运行某一条case 把光标移动到某一条case后面,然后右键,选择"Run..."来运行程序 此时,pycharm会只运行光标所在位置的这一条case 2.如果想执行全部ca ...

  7. 敏捷冲刺每日报告——Day4

    1.情况简述 Alpha阶段第一次Scrum Meeting 敏捷开发起止时间 2017.10.28 00:00 -- 2017.10.29 00:00 讨论时间地点 2017.10.28晚9:30, ...

  8. 20162302 实验三《敏捷开发与XP实践》实验报告

    实 验 报 告 课程:程序设计与数据结构 姓名:杨京典 班级:1623 学号:20162302 实验名称:敏捷开发与XP实践 实验器材:装有IdeaU的联想拯救者80RQ 实验目的与要求:1.代码的格 ...

  9. bisect 二分查找

    先说明的是,使用这个模块的函数前先确保操作的列表是已排序的. 先看看 insort  函数: 其插入的结果是不会影响原有的排序. 再看看 bisect  函数: 其目的在于查找该数值将会插入的位置并返 ...

  10. 自主学习之RxSwift(一) -----Driver

    对于RxSwift,我也是初学者,此系列来记录我学习RxSwift的历程! (一) 想必关于Drive大家一定在RxSwift的Demo中看到过,也一定有些不解,抱着一起学习的态度,来了解一下Driv ...