计算几何原理与例题

目录

一、前言

二、简单几何

1、平面切分(2020年省赛,lanqiaoOJ题号503)

2、三角形的面积(lanqiaoOJ题号1231)

3、点和直线关系(lanqiao0J题号1240)

4、点和线段关系(lanqiaoOJ题号1242)

5、点到直线距离(lanqiaoOJ题号1286)

三、凸包

1、求土包子(lanqiaoOJ题号1316)


一、前言

本文主要讲了简单几何概念(平面切分、三角形面积、点与直线、点与线段、凸包)和相关例题。

二、简单几何

1、平面切分(2020年省赛,lanqiaoOJ题号503)

【题目描述】

平面上有 N 条直线,其中第 i 条直线是 y=(Ai)x+Bi。请计算这些直线将平面分成了几个部分。

【输入描述】

第一行包含一个整数 N。以下 N 行,每行包含两个整数 Ai, Bi。

【输出描述】

一个整数代表答案。

对于50%的评测用例,1<=N<=4,-10≤A, B≤10。

对于所有评测用例,1<=N<=1000,-100000<=A, B<=100000。

【思路】

  • 先看第一条和第二条线。第一条线把平面分成 2 半,第二条线如果与第一条平行,把平面分成 3 部分;如果不平行,有 1 个交点,把平面分成 4 部分。
  • 总结:每增加一条直线,平面分割的增加数量,等于 “其与先前直线的交点数(不包括与已有交点重合的点)+ 1” 。
  • 首先对输入的直线去重,然后按上述规则统计平面被直线分成了几个部分。每加入一条直线,暴力计算它与其他直线的交点即可。
n=int(input())
line=[tuple(map(int,input().split())) for i in range(n)]
se=set(line)
line=list(se)       #去重后的线
ans=2
for i in range(1,len(line)):    #从第二条直线开始
    a1,b1=line[i]
    pos=set()       #存交点
    for j in range(i):          #第1条直线与第1~i-1条直线的关系
        a2,b2=line[j]
        if a1==a2:            #平行,无交点
            continue
        x=(b2-b1)/(a1-a2)
        y=a1*x+b1
        poss.add((x,y))
    ans+=len(pos)+1
print(ans)

2、三角形的面积(lanqiaoOJ题号1231)

【题目描述】

平面直角坐标系中有一个三角形,请你求出它的面积。

【输入描述】

第一行输入一个T,代表测试次数。每组测试输入有三行,每行一个坐标 (x, y) 代表三个点。1<=T<=10^3,-10^5<=x,y<=10^5。

【输出描述】

输出一个实数表示三角形面积。

用海伦公式编码,Python 代码会有较大误差。

from math import *
def Dist(x1,y1,x2,y2):
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))
t=int(input())
for i in range(t):
    x1,y1=map(float,input().split())
    x2,y2=map(float,input().split())
    x3,y3=map(float,input().split())
    a=Dist(x1,y1,x2,y2)
    b=Dist(x1,y1,x3,y3)
    c=Dist(x2,y2,x3,y3)
    p=(a+b+c)/2
    s=sqrt(p*(p-a)*(p-b)*(p-c))
    print("{:.2f}".format(s))

本题的正解是用叉积求面积。

【点和向量】

二维平面中的点用坐标 (x, y) 来表示。计算几何的基础是向量。有大小有方向的量,称为向量。只有大小而没有方向的量,称为标量。

用平面上的两个点可以确定一个向量,例如用起点 P1 和终点 P2 表示一个向量。·为了简化描述,可以把它平移到原点,把向量看成从原点 (0, 0) 指向点 (x,y) 的一个有向线段。向量的表示,在形式上与点的表示完全一样。

【向量的计算】

1)加:点与点的加法运算没有意义;点与向量相加得到另一个点;向量与向量相加得到另外一个向量。

2)减:两个点的差是一个向量;向量A减B,得到由B指向A的向量。

3)乘:向量与实数相乘得到等比例放大的向量。

4)除:向量与实数相除得到等比例缩小的向量。

【点积】

点积 (Dot product):记向量 A 和 B 的点积为 A+B

定义:A+B=|A||B|cosθ,其中 θ 为A、B之间的夹角。

点积的几何意义为 A 在 B 上的投影长度乘以 B 的模长。

编程时计算点积,并不需要知道 θ。如果已知 A=(A.x, A.y),B=(B.x, B.y),有:

A·B = A.x*B.x + A.y*B.y

【点积的应用】

1)判断 A 与 B 的夹角是钝角还是锐角

点积有正负,利用正负号,可以判断向量的夹角:

若 dot(A, B)>0,A 与 B 的夹角为锐角;

若 dot(A, B)<0,A 与 B 的夹角为钝角;

若 dot(A, B)=0,A 与 B 的夹角为直角。

【叉积】

叉积:AXB=|A||B|sinθ

θ 表示向量 A 旋转到向量 B 所经过的夹角。

两个向量的叉积是一个带正负号的数值。AXB 的几何意义为向量 A 和 B 形成的平行四边形的“有向”面积,这个面积有正负。

向量 A、B 的叉积 AXB:A.x*B.y-A.y*B.x

【叉积的应用】

1)判断向量 A、B 的方向关系

若 AXB>0,B 在 A 的逆时针方向;

若 AXB<0,B 在 A 的顺时针方向;

若 AXB=0,B 与 A 共线,可能是同方向的,也可能是反方向的。

2)计算两向量构成的平行四边形有向面积

三个点 A、B、C,以 A 为公共点,得到 2 个向量 B-A 和 C-A,它们构成的平行四边形,面积是:Cross(B-A, C-A);

如果以 B 或 C 为公共点构成平行四边形,面积是相等的,但是正负不一样。

3)计算三点构成的三角形的面积。三个点 A、B、C 构成的三角形面积,等于平行四边形面积的二分之一。

好了,扯了这么多,我们再回顾一下题目。

接下来用叉积求面积。

def Cross(x1,y1,x2,y2):     #叉积
    return x1*y2-y1*x2
t=int(input())
for i in range(t):
    x1,y1=map(float,input().split())
    x2,y2=map(float,input().split())
    x3,y3=map(float,input().split())
    a1=x1-x2
    a2=y1-y2
    b1=x1-x3
    b2=y1-y3
    s=Cross(a1,a2,b1,b2)/2    #用叉积算面积
    if s<0:
        s=-s                #取绝对值
    print("{:.2f}".format(s))   #或者:print("%.2f"%s)

3、点和直线关系(lanqiao0J题号1240)

【题目描述】

平面直角坐标系中有一个点 C 和一条直线 AB,求点 C 和直线 AB 的位置关系。

【输入描述】

第 1 行是整数 T,表示测试数量。每组测试输入三行,每行一个坐标 (x, y) 分别代表 A、B、C 三点。

【输出描述】

如果点 C 在直线 AB 上,输出 IN,如果点 C 在直线 AB 左侧,输出 L,如果点 C 在直线 AB 右侧输出 R。

【点和直线关系】

二维平面上,点和直线有三种位置关系:点在直线左侧、在右侧、在直线上。

用直线上的两点 p1 和 p2,与点 p 构成两个向量,用叉积的正负判断方向,就能得到位置关系。

也可以用 p、p1、p2 三点形成的三角形的面积来确定 p 和直线 p1p2 的关系。

设直线上两个点是 A、B,判断的点为 C。

S(A, B, C) 是三个点围成的三角形面积。

如果 S(A,B,C) 为正数,则 C 在直线 AB 的左侧;

如果 S(A,B,C) 为负数,则 C 在直线 AB 的右侧;

如果 S(A,B,C) 为 0,则 C 在直线 AB 上。

def Cross(x1,y1,x2,y2):         #叉积
    return x1*y2-y1*x2
t=int(input())
for _ in range(t):
    ax,ay=map(float,input().split())
    bx,by=map(float,input().split())
    cx,cy=map(float,input().split())
    x1=ax-bx
    y1=ay-by
    x2=ax-cx
    y2=ay-cy
    s=Cross(x1,y1,x2,y2)/2      #用叉积算面积
    if s>0:
        print("L")
    if s<0:
        print("R")
    if s==0:
        print("IN")

4、点和线段关系(lanqiaoOJ题号1242)

【题目描述】

平面直角坐标系中有一个点 C 和一条线段 AB,求点 C 和线段 AB 的位置关系。

【输入描述】

第 1 行是整数 T,表示测试数量。每组测试输入三行,每行一个坐标 (x, y) 分别代表 A、B、C 三点。

【输出描述】

如果点 C 在线段AB上,输出 Yes,否则输出 No。

【点和线段的关系】

判断点 p 是否在线段 v 上:

先用叉积判断是否共线;

然后用点积看 p 和 v 的两个端点产生的角是否是钝角 (实际上应该是180度角)。

def Cross(x1,y1,x2,y2):         #叉积
    return x1*y2-y1*x2
def Dot(x1,y1,x2,y2):
    return x1*x2+y1*y2          #点积
t=int(input())
for _ in range(t):
    ax,ay=map(float,input().split())
    bx,by=map(float,input().split())
    cx,cy=map(float,input().split())
    x1=ax-bx
    y1=ay-by
    x2=ax-cx
    y2=ay-cy
    if Cross(x1,y1,x2,y2)==0 and Dot(x1,y1,x2,y2)>=0:
        print('Yes')
    else:
        print('No')

5、点到直线距离(lanqiaoOJ题号1286)

【题目描述】

平面直角坐标系中有一个点 C 和一条线段 AB,求点 C 到直线 AB 的距离。

【输入描述】

第 1 行是整数 T,表示测试数量。每组测试输入三行,每行一个坐标 (x, y) 分别代表 A、B、C 三点。

【输出描述】

输出一个实数表示距离。结果保留 2 位小数。

已知点 p 和直线 v(p1,p2),求 p 到 v 的距离。

首先用叉积求 p、p1、p2 构成的平行四边形面积,然后用面积除以平行四边形的底边长,也就是线段 (p1, p2) 的长度,就得到了平行四边形的高,即 p 点到直线的距离。

from math import *
def Cross(x1,y1,x2,y2):
    return x1*y2-y1*x2      #叉积
t=int(input())
for i in range(t):
    ax,ay=map(float,input().split())
    bx,by=map(float,input().split())
    cx,cy=map(float,input().split())
    d=abs(Cross(ax-cx,ay-cy,bx-cx,by-cy))   #用叉积算面积
    w=sqrt((ax-bx)*(ax-bx)+(ay-by)*(ay-by))
    print("%.2f"%(d/w))

三、凸包

凸包问题:给定一些点,求能把所有这些点包含在内的面积最小的多边形。

Andrew 算法:两次扫描,先从最左边的点沿 “下凸包” 扫描到最右边,再从最右边的点沿 “上凸包” 扫描到最左边,“上凸包” 和 “下凸包” 合起来就是完整的凸包。

 1)把所有点按照横坐标 x 从小到大进行排序,如果 x 相同,按 y 从小到大排序。得到序列 {p0, p1, p2, ..., pm}。

2)从左到右扫描所有点,求 “下凸包”。p0 一定在凸包上,它是凸包的最左边的顶点,从 p0 开始,依次检查 {p1, p2, ..., pm},扩展出 “下凸包”。

3)从右到左重新扫描所有点,求 “上凸包”。

1、求土包子(lanqiaoOJ题号1316)

【题目描述】

平面直角坐标系中求一个凸包的周长 C。

【输入描述】

第 1 行是整数 n,接下来输入 n 行,每行一个坐标 (x, y)。n<5×10^4。

【输出描述】

输出一个实数 C,结果保留 6 位小数。

from decimal import *
from math import *

class Point:
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y
    def __sub__(self,other):
        return Point(self.x-other.x,self.y-other.y)

def cross(a,b):
    return a.x*b.y-b.x*a.y
def square(x):
    return x*x
def dis2(a,b):
    return square(a.x-b.x)+square(a.y-b.y)
def dis(a,b):
    return sqrt(dis2(a,b))

def convex_hull(p):   
    def cmp(a):      #把所有点按照横坐标x从小到大进行排序,如果x相同,按y从小到大排序
        return a.x*Decimal(1e9)+a.y
    p=sorted(p,key=cmp)
    n=len(p)
    stk=[]           #用stk记录凸包上的点
    stk.append(p[0])
    for i in p[1:]:  #从左到右扫描所有点,求“下凸包”
        while len(stk)>1 and cross(stk[-1]-stk[-2],i-stk[-2])<=0:
            del stk[-1]
        stk.append(i)
    tmp=len(stk)
    for i in p[::-1]:  #从右到左重新扫描所有点,求“上凸包”
        while len(stk)>tmp and cross(stk[-1]-stk[-2],i-stk[-2])<=0:
            del stk[-1]
        stk.append(i)
    stk.pop()
    return stk
        
n=int(input())
p=[Point() for i in range(n)]
for i in p:
    i.x,i.y=map(Decimal,input().split())
convex=convex_hull(p)
C=0
for i in range(len(convex)):
    C+=dis(convex[i-1],convex[i])
print("%.6f"%C)

以上,计算几何

祝好

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吕飞雨的头发不能秃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值