Hard
题目描述
有几个方块掉落在二维平面的 X 轴上。
给定一个二维整数数组 positions,其中 positions[i] = [lefti, sideLengthi] 表示第 i 个边长为 sideLengthi 的方块,其左边缘与 X 坐标 lefti 对齐后掉落。
每个方块按顺序从高于任何已落地方块的高度掉落。然后它向下坠落(负 Y 方向),直到落在另一个方块的顶面或 X 轴上。方块擦过另一个方块的左/右侧不算落在它上面。一旦落地,方块就会冻结在原地,无法移动。
在每个方块掉落后,你需要记录当前最高的方块堆叠的高度。
返回一个整数数组 ans,其中 ans[i] 表示第 i 个方块掉落后上述描述的高度。
示例 1:
输入:positions = [[1,2],[2,3],[6,1]]
输出:[2,5,5]
解释:
第一次掉落后,最高的堆叠是方块 1,高度为 2。
第二次掉落后,最高的堆叠是方块 1 和 2,高度为 5。
第三次掉落后,最高的堆叠仍是方块 1 和 2,高度为 5。
因此返回 [2, 5, 5]。
示例 2:
输入:positions = [[100,100],[200,100]]
输出:[100,100]
解释:
第一次掉落后,最高的堆叠是方块 1,高度为 100。
第二次掉落后,最高的堆叠是方块 1 或方块 2,都是高度 100。
因此返回 [100, 100]。
注意方块 2 只是擦过方块 1 的右侧,这不算落在它上面。
约束条件:
1 <= positions.length <= 10001 <= lefti <= 10^81 <= sideLengthi <= 10^6
解题思路
这道题的核心是模拟方块下降过程,需要维护每个位置的高度信息。
思路分析:
暴力模拟法:对于每个掉落的方块,遍历之前所有的方块,找出与当前方块在 X 轴上有重叠的方块,取最高的高度作为当前方块的底部高度。
坐标压缩 + 线段树:由于坐标范围很大(最大 10^8),直接建立数组会超内存。可以使用坐标压缩,将所有可能的坐标映射到较小的范围,然后用线段树维护区间最大值。
有序集合优化:维护已放置方块的信息,对于新方块,只需检查与其有重叠的方块。
推荐解法:暴力模拟
由于 n ≤ 1000,暴力模拟的时间复杂度 O(n²) 是可以接受的。对于每个新方块:
- 计算其左右边界 [left, right)
- 遍历之前的所有方块,找出与当前方块有重叠的方块
- 在这些重叠方块中找到最高的高度
- 当前方块的高度 = 最高重叠高度 + 当前方块边长
- 更新全局最大高度
两个方块重叠的条件:方块1的区间 [a, b) 和方块2的区间 [c, d) 重叠当且仅当 max(a,c) < min(b,d)。
代码实现
class Solution {
public:
vector<int> fallingSquares(vector<vector<int>>& positions) {
vector<int> result;
vector<vector<int>> squares; // [left, right, height]
int maxHeight = 0;
for (auto& pos : positions) {
int left = pos[0];
int side = pos[1];
int right = left + side;
int baseHeight = 0;
// 找到与当前方块重叠的所有方块中的最大高度
for (auto& square : squares) {
int sLeft = square[0], sRight = square[1], sHeight = square[2];
// 检查是否重叠:max(left, sLeft) < min(right, sRight)
if (max(left, sLeft) < min(right, sRight)) {
baseHeight = max(baseHeight, sHeight);
}
}
int newHeight = baseHeight + side;
squares.push_back({left, right, newHeight});
maxHeight = max(maxHeight, newHeight);
result.push_back(maxHeight);
}
return result;
}
};
class Solution:
def fallingSquares(self, positions: List[List[int]]) -> List[int]:
result = []
squares = [] # [left, right, height]
max_height = 0
for left, side in positions:
right = left + side
base_height = 0
# 找到与当前方块重叠的所有方块中的最大高度
for s_left, s_right, s_height in squares:
# 检查是否重叠
if max(left, s_left) < min(right, s_right):
base_height = max(base_height, s_height)
new_height = base_height + side
squares.append([left, right, new_height])
max_height = max(max_height, new_height)
result.append(max_height)
return result
public class Solution {
public IList<int> FallingSquares(int[][] positions) {
var result = new List<int>();
var squares = new List<int[]>(); // [left, right, height]
int maxHeight = 0;
foreach (var pos in positions) {
int left = pos[0];
int side = pos[1];
int right = left + side;
int baseHeight = 0;
// 找到与当前方块重叠的所有方块中的最大高度
foreach (var square in squares) {
int sLeft = square[0], sRight = square[1], sHeight = square[2];
// 检查是否重叠
if (Math.Max(left, sLeft) < Math.Min(right, sRight)) {
baseHeight = Math.Max(baseHeight, sHeight);
}
}
int newHeight = baseHeight + side;
squares.Add(new int[]{left, right, newHeight});
maxHeight = Math.Max(maxHeight, newHeight);
result.Add(maxHeight);
}
return result;
}
}
var fallingSquares = function(positions) {
const result = [];
const squares = []; // [left, right, height]
let maxHeight = 0;
for (const [left, side] of positions) {
const right = left + side;
let baseHeight = 0;
// 找到与当前方块重叠的所有方块中的最大高度
for (const [sLeft, sRight, sHeight] of squares) {
// 检查是否重叠
if (Math.max(left, sLeft) < Math.min(right, sRight)) {
baseHeight = Math.max(baseHeight, sHeight);
}
}
const newHeight = baseHeight + side;
squares.push([left, right, newHeight]);
maxHeight = Math.max(maxHeight, newHeight);
result.push(maxHeight);
}
return result;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 对于每个方块,需要检查之前的所有方块 |
| 空间复杂度 | O(n) | 存储所有方块的信息 |
相关题目
- . The Skyline Problem (Hard)