LeetCode 3382. Maximum Area Rectangle With Point Constraints II
weekly contest 427。
題目
有 n 個座標點在無限平面上。
輸入整數陣列 xCoord 和 yCoord,其中 (xCoord[i], yCoord[i]) 代表第 i 個點的座標。
你的目標是找到最大的矩形面積,滿足:
- 四個頂點必須是陣列中的座標點。
- 在矩形內部或是邊界上不可有其他點。
- 矩形的邊與座標軸平行。
回傳最大面積,若不存在則回傳 -1。
解法
跟 Q2 差不多,只是點的數量變成 2e5,座標值域達 8e7,有夠大。
看個幾個大神的作法,挑一種最好理解的方式。
首先將點以 x 軸遞增排序,能保證相同 x 軸的點都會聚集在一起。
然後再以 y 軸遞增排序,能 x 軸的相同的點,會以 y 軸遞增的順序相鄰。
排序後,枚舉所有點作為矩形的右上角 (x2, y2),保證先前遇過的所有點都在左方或是正下方。
若當前 points[i] 點 (x2, y2) 的正下方存在其他點,必為 (x2, y1),排序後的索引是 points[i-1]。
此時確定矩形的上下界為 y2, y1。
雖然無法直接算出矩形內的點,也不知道左邊界在哪裡。
但是以左下角 (0, y1) 到右上角 (x2, y2),只需要一維的區間查詢資料結構即可。
按照這個順序,只要找到前一個上下界相同的矩形,比對一下點的數量,就知道矩形內有多少點。
例如:
前一個矩形的右上、右下為 (x1, y2), (x1, y1),有 4 個點。
當前矩形的右上、右下為 (x2, y2), (x2, y1),有 6 個點。
根據矩形的枚舉方式,右上、右下點之間必不含其他點。若正好多出 2 點可保證矩形內沒有其他點,以此面積更新答案。
在枚舉右上角的過程中,還需要逐步將點加入資料結構。
每次只會影響一個 y 軸,只需要單點修改,可以選擇用樹狀陣列。
而且 y 軸的值域過大,需要做離散化。
找到右下角時,先檢查是否有前一個矩形、點數變化判斷,嘗試更新答案。
最後將當前矩形的點數與 x 座標保存,以供後續使用。
時間複雜度 O(N log N)。
空間複雜度 O(N)。
class Solution:
def maxRectangleArea(self, xCoord: List[int], yCoord: List[int]) -> int:
N = len(xCoord)
sorted_y = sorted(set(yCoord))
mp_y = {x: i for i, x in enumerate(sorted_y)}
points = list(zip(xCoord, yCoord))
points.sort() # sort by x-axis, then by y-axis
bit = BIT(N+5)
ans = -1
# previous rect area with (y1, y2)
# which rightbound is x1, and has p_cnt points
# seen[(y1, y2)] = [x1, p_cnt]
seen = {}
for i, (x2, y2) in enumerate(points):
# count point (x2, y2)
y2 = mp_y[y2]
bit.update(y2, 1)
# find rectangle
# must find lower right (x2, y1) first
if i > 0 and points[i-1][0] == x2:
y1 = mp_y[points[i-1][1]]
p_cnt = bit.query_range(y1, y2)
key = (y1, y2)
# check if rect valid
if key in seen and seen[key][1] + 2 == p_cnt:
x1 = seen[key][0]
w = x2 - x1
h = sorted_y[y2] - sorted_y[y1]
ans = max(ans, w*h)
# update points count
seen[key] = (x2, p_cnt)
return ans
class BIT:
"""
tree[0]代表空區間,不可存值,基本情況下只有[1, n-1]可以存值。
offset為索引偏移量,若設置為1時正好可以對應普通陣列的索引操作。
"""
def __init__(self, n, offset=1):
self.offset = offset
self.tree = [0]*(n+offset)
def update(self, pos, val):
"""
將tree[pos]增加val
"""
i = pos+self.offset
while i < len(self.tree):
self.tree[i] += val
i += i & (-i)
def query(self, pos):
"""
查詢[1, pos]的前綴和
"""
i = pos+self.offset
res = 0
while i > 0:
res += self.tree[i]
i -= i & (-i)
return res
def query_range(self, i, j):
"""
查詢[i, j]的前綴和
"""
return self.query(j)-self.query(i-1)