1. 程式人生 > >Leetcode 963:最小面積矩形 II(超詳細的解法!!!)

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. 1 <= points.length <= 50
  2. 0 <= points[i][0] <= 40000
  3. 0 <= points[i][1] <= 40000
  4. 所有的點都是不同的。
  5. 與真實值誤差不超過 10^-5 的答案將視為正確結果。

解題思路

首先想到的解法就是暴力破解,我們先找三個點,首先判斷這三個點是不是可以構成直角三角形,如果可以的話,我們再去確定第四個點

  • x 4 = x
    1 + x 3 x 2 x_4=x_1+x_3-x_2
  • y 4 = y 1 + y 3 y 2 y_4=y_1+y_3-y_2

我們要判斷這個座標是不是在陣列中,如果在的話,我們就確定了一個矩形,那麼我們就可以計算面積。以此類推,計算所有矩形的面積,去最後所有矩形面積的最小值即可。

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

如有問題,希望大家指出!!!