📅  最后修改于: 2023-12-03 15:28:48.718000             🧑  作者: Mango
本题来源于 ACM/ICPC 2008 年的门户网站学科赛题库,是一道计算几何的题目。该题目编号为 第 64 题,题目名称为“门|门”。
在平面直角坐标系中,给定若干个点的坐标,其中存在两个点 $(x_1, y_1)$ 和 $(x_2, y_2)$,它们组成的线段完全垂直于 $x$ 轴且它们的距离最短。请编写程序找出这个距离最短的线段,输出其距离值。
第一行输入一个正整数 $n$,表示给定的点数 $(2 \leq n \leq 10^5)$。
接下来的 $n$ 行,每行包含两个整数 $x$ 和 $y$,表示给定点的坐标 $(x, y)$($-10^9 \leq x, y \leq 10^9$)。
输出一个实数,表示距离最短的线段的长度,结果精确到小数点后 $2$ 位。
5
0 0
1 1
2 2
3 3
4 4
1.41
该算法是一个比较经典的计算几何问题,解决该问题的算法为平面点集最小距离算法。在本题中,我们需找出组成的线段完全垂直于 $x$ 轴,因此可以采用旋转卡壳的方法。
旋转卡壳的实现过程主要是以下几步:
下面是 Python 语言的代码实现:
import math
def dist(p1, p2):
return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)
def rotate_calipers(pairs):
def cal_dist(p1, p2):
return (p1[0]-p2[0])**2 + (p1[1]-p2[1])**2
def cal_angle(p1, p2):
return (p2[1]-p1[1]) / math.sqrt(cal_dist(p1, p2))
def rotate(p1, p2, angle):
xp = (p2[0] - p1[0]) * math.cos(angle) - (p2[1] - p1[1]) * math.sin(angle) + p1[0]
yp = (p2[0] - p1[0]) * math.sin(angle) + (p2[1] - p1[1]) * math.cos(angle) + p1[1]
return (xp, yp)
n = len(pairs)
l, r = 0, 0
for i in range(n):
if pairs[i][0] < pairs[l][0]:
l = i
if pairs[i][0] > pairs[r][0]:
r = i
ans = dist(pairs[l], pairs[r])
k = float('inf')
while True:
k = min(k, cal_angle(pairs[l], pairs[r]))
r1 = (r + 1) % n
l1 = (l + 1) % n
if cal_dist(pairs[l], pairs[r1]) > cal_dist(pairs[l1], pairs[r]):
l = l1
else:
r = r1
if dist(pairs[l], pairs[r]) < ans:
ans = dist(pairs[l], pairs[r])
if l == 0 and r == 0:
break
return ans
if __name__ == '__main__':
n = int(input())
pairs = []
for i in range(n):
x, y = map(int, input().split())
pairs.append((x, y))
print("{:.2f}".format(rotate_calipers(pairs)))
该程序实现了旋转卡壳算法,输入点集的数量和坐标,然后输出最短的线段的长度。