Leetcode 963:最小面積矩形 II(超詳細的解法!!!)
給定在 xy 平面上的一組點,確定由這些點組成的任何矩形的最小面積,其中矩形的邊不一定平行於 x 軸和 y 軸。
如果沒有任何矩形,就返回 0。
示例 1:
輸入:[[1,2],[2,1],[1,0],[0,1]]
輸出:2.00000
解釋:最小面積的矩形出現在 [1,2],[2,1],[1,0],[0,1] 處,面積為 2。
示例 2:
輸入:[[0,1],[2,1],[1,1],[1,0],[2,0]]
輸出:1.00000
解釋:最小面積的矩形出現在 [1,0],[1,1],[2,1],[2,0] 處,面積為 1。
示例 3:
輸入:[[0,3],[1,2],[3,1],[1,3],[2,1]]
輸出:0
解釋:沒法從這些點中組成任何矩形。
示例 4:
輸入:[[3,1],[1,1],[0,1],[2,1],[3,3],[3,2],[0,2],[2,3]]
輸出:2.00000
解釋:最小面積的矩形出現在 [2,1],[2,3],[3,3],[3,1] 處,面積為 2。
提示:
1 <= points.length <= 50
0 <= points[i][0] <= 40000
0 <= points[i][1] <= 40000
- 所有的點都是不同的。
- 與真實值誤差不超過
10^-5
的答案將視為正確結果。
解題思路
首先想到的解法就是暴力破解,我們先找三個點,首先判斷這三個點是不是可以構成直角三角形,如果可以的話,我們再去確定第四個點
我們要判斷這個座標是不是在陣列中,如果在的話,我們就確定了一個矩形,那麼我們就可以計算面積。以此類推,計算所有矩形的面積,去最後所有矩形面積的最小值即可。
class Solution:
def minAreaFreeRect(self, points):
"""
:type points: List[List[int]]
:rtype: float
"""
res = float('inf')
mem = set(map(tuple, points))
def dis(p1, p2):
return (p1[0]-p2[0])**2 + (p1[1] - p2[1])**2
for (x1, y1) in points:
for (x2, y2) in points:
if x1 == x2 and y1 == y2:
continue
for (x3, y3) in points:
d1 = dis((x1,y1), (x2,y2))
d2 = dis((x1,y1), (x3,y3))
d3 = dis((x2,y2), (x3,y3))
if d1 + d3 != d2:
continue
x4 = x1+x3-x2
y4 = y1+y3-y2
if (x4,y4) not in mem:
continue
area = pow(d1, 0.5) * pow(d3, 0.5)
if area > 0:
res = min(res, area)
return res if res != float('inf') else 0
但是這種做法超時了。還有一種暴力破解的思路,就是現將所有四個點的組合枚舉出來,然後判斷這四個點是不是可以構成矩形,如果可以的話,計算面積,取所有面積的最小值即可。關於如何判斷矩形,可以看這篇判斷四個點是否可以構成矩形(優雅的解法!!!)。有沒有什麼更好的解法呢?我們可以先取出三個點的組合,然後假設這三個點可以構成矩形(並且假設其中一組點是對角),再通過上列中的兩個公式求解出第四個點的位置,最後通過向量點積確定是否存在直角,如果存在,那麼這個矩形就存在,否則的話矩形不存在。
class Solution:
def minAreaFreeRect(self, points):
"""
:type points: List[List[int]]
:rtype: float
"""
points = set(map(tuple, points))
res = float('inf')
for p1, p2, p3 in itertools.combinations(points, 3):
p4 = p2[0] + p3[0] - p1[0], p2[1] + p3[1] - p1[1]
if p4 in points:
v21 = p2[0] - p1[0], p2[1] - p1[1]
v31 = p3[0] - p1[0], p3[1] - p1[1]
if abs(v21[0] * v31[0] + v21[1] * v31[1]) < 1e-5:
area = abs(v21[1] * v31[0] - v21[0] * v31[1])
if area < res:
res = area
return res if res != float('inf') else 0
通過向量計算的方式,我們加快了速度。但是這種演算法依舊是O(n^3)
級別的,有沒有更好的?對於最後一個例子
我們可以先取出兩個點,然後計算兩個點的中點,我們建立一個dict
存放相同斜率點的中點。
然後我們遍歷dict
,然後從dict.val
中再取出兩個點,這兩個點一定來自於相同斜率的兩邊,我們再計算這兩個點的斜率,如果和之前的邊斜率相垂直,那麼原來的兩邊(四個點)必能構成矩形。按照此法計算所有dict
中的值,最後取面積的最小值即可。
class Solution:
def minAreaFreeRect(self, points):
"""
:type points: List[List[int]]
:rtype: float
"""
points = [complex(*point) for point in sorted(points)]
mem = collections.defaultdict(list)
for p, q in itertools.combinations(points, 2):
mem[q - p].append((p + q) / 2)
res = float('inf')
for v1, mid_points in mem.items():
for a, b in itertools.combinations(mid_points, 2):
v2 = a - b
if abs(v1.real * v2.real + v1.imag * v2.imag) < 1e-5:
res = min(res, abs(v1) * abs(v2))
return res if res != float('inf') else 0
reference:
https://leetcode.com/problems/minimum-area-rectangle-ii/solution/
我將該問題的其他語言版本新增到了我的GitHub Leetcode
如有問題,希望大家指出!!!