hdu 2823(旋转卡壳+线段相交判断+点在多边形内判断)

本文探讨了在给定两个点集的情况下,如何高效地计算它们之间的最小距离。通过凸包算法,首先对点集进行凸包处理,然后通过一系列条件判断和几何计算,确定是否存在相交或包含关系,从而简化距离计算过程。最终,通过旋转卡壳算法精确计算两个凸包间的最小距离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给出两个点集p和q,求这两个点集之间的最小距离,如果p与q相交,或者p包含q,或者q包含p,则输出0.0000。

思路:先对两个点集各求一次凸包得到s,t两个凸包,用得到的凸包先判断两个凸包是否相交,分别枚举凸包t的每条边是否与s中的边相交,s中的每条边是否与t中的每条边相交,如果相交则输出0.0000,否则再判断两个凸包是否有包含关系,枚举s上的点是否在t内,如果有就跳出输出0.0000,没有的话再枚举t上的点是否在s内,如果有就跳出输出0.0000,没有的话就对两个凸包进行旋转卡壳求两个凸包的最小距离。

#include<iostream>  
#include<algorithm>  
#include<string.h>  
#include<stack>  
#include<queue>  
#include<math.h>  
#include<cstdio>  
  
using namespace std; 
 
const double PI = acos(-1.0);
const double MAX=1e9;
const double eps=1e-9;  
   
struct point  
{  
    double x,y;  
    point () {}  
    point (double xx,double yy): x(xx),y(yy) {}  
}p1[10010],p2[10010],res1[10010],res2[10010];  
struct Segment  
{  
    point a,b;
    Segment(){}
    Segment(point a,point b):a(a),b(b) {}
};   

point operator + (point a,point b) { return point(a.x+b.x,a.y+b.y); }  
  
point operator - (point a,point b) { return point(a.x-b.x,a.y-b.y); }  
  
point operator * (point a,point b) { return point(a.x*b.x,a.y*b.y); }  
  
point operator / (point a,point b) { return point(a.x/b.x,a.y/b.y); }  
  
double cross(point a,point b,point op)  
{  
    return (op.x-a.x)*(op.y-b.y)-(op.y-a.y)*(op.x-b.x);   
}  
double dot(point a,point b)  
{  
    return a.x*b.x+a.y*b.y;   
}  
double dist_p_to_p(point a,point b)  
{  
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));     
}  

bool inter(point a,point b,point c,point d)
{
	if( max(a.x,b.x)>=min(c.x,d.x) &&
		max(c.x,d.x)>=min(a.x,b.x) &&
		max(a.y,b.y)>=min(c.y,d.y) &&
		max(c.y,d.y)>=min(a.y,b.y) &&
		cross(a,b,c)*cross(a,b,d)<=0 &&
		cross(c,d,a)*cross(c,d,b)<=0 )
		return true;
	else return false;
}

double dist_p_to_seg(point c,point a,point b)  
{  
    point ab=b-a;  
    point ac=c-a;  
    double f=dot(ab,ac);  
    if(f<0) return dist_p_to_p(a,c);  
    double D=dot(ab,ab);  
    if(f>D) return dist_p_to_p(b,c);  
    f=f/D;  
    point d=point(a.x+f*ab.x,a.y+f*ab.y);  
    return dist_p_to_p(d,c);  
}  
double dist_seg_to_seg(point p1, point p2, point p3, point p4)  
{  
    return min(min(dist_p_to_seg(p1, p3, p4), dist_p_to_seg(p2, p3, p4)),  
     min(dist_p_to_seg(p3, p1, p2), dist_p_to_seg(p4, p1, p2)));  
}  

bool mult(point sp, point ep, point op)  
{  
    return (sp.x - op.x) * (ep.y - op.y)  
    >= (ep.x - op.x) * (sp.y - op.y);  
}  
  
bool operator < (const point &l, const point &r)  
{  
    return l.y < r.y || (l.y == r.y && l.x < r.x);  
}  
int graham(point pnt[], int n, point res[])  
{  
    int i, len, k = 0, top = 1;  
    sort(pnt, pnt + n);  
    if (n == 0) return 0; res[0] = pnt[0];  
    if (n == 1) return 1; res[1] = pnt[1];  
    if (n == 2) return 2; res[2] = pnt[2];  
    for (i = 2; i < n; i++)   
    {  
        while (top && mult(pnt[i], res[top], res[top-1]))  
            top--;  
        res[++top] = pnt[i];  
    }  
    len = top; res[++top] = pnt[n - 2];  
    for (i = n - 3; i >= 0; i--)   
    {  
        while (top!=len && mult(pnt[i], res[top],  
            res[top-1])) top--;  
        res[++top] = pnt[i];  
    }  
    return top;  
}  
double rc(point pp[],point qq[],int n,int m)  
{  
    int q=0;  
    int p=0;  
    for(int i=0;i<n;i++)   
        if(pp[i].y-pp[p].y<-eps)  
            p=i;  
    for(int i=0;i<m;i++)  
        if(qq[i].y-qq[q].y>eps)  
            q=i;  
    pp[n]=pp[0];  
    qq[m]=qq[0];  
    double tmp,ans=1e99;  
    for(int i=0;i<n;i++)  
    {  
        while((tmp=cross(pp[p+1],qq[q+1],pp[p])-cross(pp[p+1],qq[q],pp[p]))>eps)  
            q=(q+1)%m;  
        if(tmp<-eps)  
            ans=min(ans,dist_p_to_seg(qq[q],pp[p],pp[p+1]));  
        else  
            ans=min(ans,dist_seg_to_seg(pp[p],pp[p+1],qq[q],qq[q+1]));  
        p=(p+1)%n;  
    }  
    return ans;  
              
}  
 
bool is_online(point p, Segment s)
{  
    if(fabs(cross(s.a, p, s.b)) <= eps && min(s.a.x, s.b.x) <= p.x &&  
            p.x <= max(s.a.x, s.b.x) && min(s.a.y, s.b.y) <= p.y && p.y <= max(s.a.y, s.b.y))  
        return true;  
    return false;  
}  

bool is_segment_crossing(Segment u, Segment v)
{  
    return((max(u.a.x, u.b.x) >= min(v.a.x, v.b.x)) &&  
           (max(v.a.x, v.b.x) >= min(u.a.x, u.b.x)) &&  
           (max(u.a.y, u.b.y) >= min(v.a.y, v.b.y)) &&  
           (max(v.a.y, v.b.y) >= min(u.a.y, u.b.y)) &&  
           (cross(v.a, u.b, u.a) * cross(u.b, v.b, u.a) >= 0) &&  
           (cross(u.a, v.b, v.a) * cross(v.b, u.b, v.a) >= 0));  
  
}  
bool inPolygon(point p, point s[], int len) /*判断点p是否在以边点顺序排列的点集s所围成的多边形内*/  
{  
    Segment seg;  
    Segment pp;  
    int num = 0;  
    point MM;/*定义无穷点*/  
    MM.y = p.y;  
    MM.x = MAX;  
    pp.a = p;  
    pp.b = MM; /*构造一条平行于x轴的以p为端点的右射线pp*/  
    for(int i = 0; i < len; i++)  
    {  
        seg.a = s[i];  
        seg.b = s[(i+1)%len];  
        if(is_online(p, seg)) /*点p在边上*/  
            return true;  
        if(fabs(seg.a.y - seg.b.y) < eps) /*水平*/  
            continue;  
        if(is_online(seg.a, pp)) /*边的一端点在射线上*/  
        {  
            if(seg.a.y > seg.b.y)  
                num++;  
        }  
        else if(is_online(seg.b, pp))  
        {  
            if(seg.b.y > seg.a.y)  
                num++;  
        }  
        else if(is_segment_crossing(pp, seg))  
            num++;  
    }    
    if(num % 2 == 1)  
        return true;  
    return false;  
}  
int main()  
{  
    int n,m;  
    while(~scanf("%d%d",&n,&m))  
    {  
        for(int i=0;i<n;i++)  
            scanf("%lf%lf",&p1[i].x,&p1[i].y);  
        for(int i=0;i<m;i++)  
            scanf("%lf%lf",&p2[i].x,&p2[i].y);  
        if(n==1 && m==1)
        {
			printf("%.4lf\n",dist_p_to_p(p1[0],p2[0]));
			continue;
		}
        int cnt1=graham(p1,n,res1);
        int cnt2=graham(p2,m,res2);
        res1[cnt1]=res1[0];
        res2[cnt2]=res2[0];
        bool flag=0;
        for(int i=0;i<cnt1;i++)
        {
        	for(int j=0;j<cnt2;j++)
        	{
				if(inter(res1[i],res1[i+1],res2[j],res2[j+1]))	
				{
					flag=1;
					break;
				}
				
			}
			if(flag) break;
		}
		if(!flag)
		{
			bool f=0;
			for(int i=0;i<cnt1;i++)
				if(inPolygon(res1[i],res2,cnt2))	
				{
					f=1;
					break;
				}
			if(f)
			{
				puts("0.0000");
				continue;
			}
			for(int i=0;i<cnt2;i++)
				if(inPolygon(res2[i],res1,cnt1))
				{
					f=1;
					break;
				}
			if(f)
			{
				puts("0.0000");
				continue;
			}
			double ans=min(rc(res1,res2,cnt1,cnt2),rc(res2,res1,cnt2,cnt1));  
	        printf("%.4lf\n",ans);  
		}
		else puts("0.0000");
	}  
    return 0;  
}
/*
3 3
0 0
9 0
0 9
1 1
1 5
5 1

3 3
0 0
0 9
9 0
9 0
0 9
10 10

3 3
0 0
9 0
0 9
5 0
10 0
10 10
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值