📜  门|门CS 2008 |第 56 题(1)

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

门|门CS 2008 | 第 56 题

这是一道经典的计算几何题目,要求求出一个凸包(Convex Hull)的面积。以下是详细的题目描述以及解决方案。

题目描述

给定一个平面上的点集,求出它们组成的凸包的面积,保证所有给定的点不重复。

具体的输入格式如下:

第一行包含一个正整数 $n$,表示点集中有 $n$ 个点。

接下来的 $n$ 行,每行包含两个实数 $x_i$ 和 $y_i$,表示该点的横纵坐标。

最后输出凸包的面积,结果保留两位小数(四舍五入)。

解决方案
凸包定义

凸包(Convex Hull)是包含给定点集中所有点的最小凸多边形,简单来说,就是将所有的点用一条线包围起来,且该线的两端点为给定点集中的某些点。

凸包的求法

基于扫描线的求解

扫描线法是计算几何中常用的一种方法,它的核心思想是把计算几何问题抽象成线性问题。

以下是扫描线法求凸包的详细步骤:

  1. 首先,我们按照 $y$ 轴坐标从小到大的次序将所有点排序。

  2. 然后,我们设定一条扫描线从下往上扫描过去,并维护一个栈来存储凸包的点集。

  3. 初始化时,我们将前两个点加入栈中。

  4. 之后每次扫描到一个新的点,我们都看看该点和栈顶的两个点是否形成了一个左转,如果是,则该点也加入栈中;否则弹出栈顶元素,直到栈顶两个点能够和该点形成一个左转。一直这样重复下去,直到扫描结束。

具体实现过程可以参考以下代码:

  def monotone_chain(points):
      points = sorted(points, key=lambda p: (p[0], p[1]))
      upper, lower = [], []
      for p in points:
          while len(upper) >= 2 and (upper[-1][0] - upper[-2][0]) * (p[1] - upper[-2][1]) - \
                  (upper[-1][1] - upper[-2][1]) * (p[0] - upper[-2][0]) < 0:
              upper.pop()
          while len(lower) >= 2 and (lower[-1][0] - lower[-2][0]) * (p[1] - lower[-2][1]) - \
                  (lower[-1][1] - lower[-2][1]) * (p[0] - lower[-2][0]) > 0:
              lower.pop()
          upper.append(p)
          lower.append(p)
      return upper + lower[-2:0:-1]

  def calc_convex_hull_area(points):
      if len(points) <= 2:
          return 0
      ch = monotone_chain(points)
      n = len(ch)
      s = 0
      for i in range(n):
          s += (ch[i][0] - ch[(i - 1 + n) % n][0]) * (ch[i][1] + ch[(i - 1 + n) % n][1])
      return abs(s) / 2
时间复杂度

通过思考可知,该方法的时间复杂度为 $O(n \log n)$,其中 $n$ 为点集的大小。因为排序的时间复杂度为 $O(n \log n)$,而扫描步骤的时间复杂度为 $O(n)$。所以总体上的时间复杂度为 $O(n \log n)$。

总结

凸包是计算几何中的一个非常基础的概念,涉及到算法的实现和时间复杂度的分析。实际工程中,很多时候需要求解凸包,因此掌握好这个概念和其求解方法是非常有益的。