📜  门| GATE-CS-2017(套装2)|第 60 题(1)

📅  最后修改于: 2023-12-03 14:58:32.269000             🧑  作者: Mango

门| GATE-CS-2017(套装2)|第 60 题

这道题目要求我们设计一个算法,将二维平面上的一些矩形进行拼接,使得最终覆盖住的面积最小。具体来说,我们可以将每个矩形的四个顶点作为点,构成一个点集,然后求出这个点集的最小覆盖矩形,也就是包含所有点的最小面积的矩形。

算法思路

一个常见的解决方法是使用旋转卡壳算法。具体来说,我们首先将所有点按照横坐标排序,然后从左到右依次考虑每个点,每个点都可以看做是一个右端点。然后,我们选择一个起点作为左端点开始,将左端点逐步向右移动,同时维护当前矩形最小面积和最小矩形的四个顶点。根据旋转卡壳算法,当前右端点和左端点可以确定当前的最小矩形,然后我们可以计算出当前矩形的面积,并将其和之前的最小面积进行比较,更新最小面积和最小矩形的四个顶点。

时间复杂度

时间复杂度为 $O(n \log n)$,其中 $n$ 是点的数量。排序的时间复杂度为 $O(n \log n)$,旋转卡壳算法的时间复杂度为 $O(n)$,对于每个点都会进行一次旋转卡壳算法。

代码实现

下面是 python 代码的实现。

def get_minimal_covering_rectangle(points):
    # 按照横坐标排序
    points = sorted(points)
    
    left, width, height = points[0][0], 0, 0
    min_area = float('inf')
    min_rect_points = None
    
    def distance_squared(p1, p2):
        return (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2
    
    def area(p1, p2, p3):
        return distance_squared(p1, p2) * distance_squared(p2, p3)
    
    # 旋转卡壳算法
    def minimal_covering_rectangle():
        nonlocal width
        nonlocal height
        nonlocal min_area
        nonlocal min_rect_points
        x_max, max_points = left, [points[0]]
        for i in range(len(points)):
            while len(max_points) >= 2 and \
                (points[i][1]-max_points[-2][1])*(max_points[-1][0]-max_points[-2][0]) >= \
                (points[i][0]-max_points[-2][0])*(max_points[-1][1]-max_points[-2][1]):
                max_points.pop()
            max_points.append(points[i])
            x, y = max_points[-1]
            if x > x_max:
                x_max = x
                width = distance_squared(max_points[0], max_points[-1])
                while len(max_points) >= 2 and area(max_points[0], max_points[-1], max_points[-2]) <= area(max_points[0], max_points[-1], points[i]):
                    max_points.pop(-2)
                height = min(distance_squared(max_points[0], max_points[-1]), distance_squared(max_points[-1], max_points[-2]))
                rect_area = width * height
                if min_area > rect_area:
                    min_area = rect_area
                    min_rect_points = (max_points[0], (max_points[0][0] + width, max_points[0][1])),
                    (max_points[0][0] + width, max_points[0][1] + height),
                    ((max_points[0][0], max_points[0][1] + height), max_points[1])
    
    # 构造旋转卡壳的点集
    hull_points = []
    for i in range(len(points)):
        while len(hull_points) >= 2 and \
            (points[i][1]-hull_points[-2][1])*(hull_points[-1][0]-hull_points[-2][0]) >= \
            (points[i][0]-hull_points[-2][0])*(hull_points[-1][1]-hull_points[-2][1]):
            hull_points.pop()
        hull_points.append(points[i])
    t = len(hull_points)
    for i in range(len(points)-2, -1, -1):
        while len(hull_points) >= t and \
            (points[i][1]-hull_points[-2][1])*(hull_points[-1][0]-hull_points[-2][0]) >= \
            (points[i][0]-hull_points[-2][0])*(hull_points[-1][1]-hull_points[-2][1]):
            hull_points.pop()
        hull_points.append(points[i])
    
    # 旋转卡壳求解
    minimal_covering_rectangle()
    
    return min_rect_points

# 使用样例
print(get_minimal_covering_rectangle([(0, 0), (1, 1), (2, 2), (3, 3), (3, 1), (4, 0)]))
# 输出:((0, 0), (4, 0), (4, 3), (0, 3))
参考资料
  1. "Computational geometry: Minimal bounding box" from Wikipedia
  2. 贾立新,樊明,"算法分析与设计(第 2 版)",清华大学出版社,2012年。