2018上海大都会赛 C题 Rescue

补完题后忙着补其他的了,就光把代码贴出来了,突然发现有人看,赶忙把题解发出来。

题意:求两条线段的最近距离。

题解:

首先求空间中两条异面直线的距离为d,求的方法是板子。。。

然后分两种情况:

一是,首先我们定义两条直线的距离的方向为v,也就是说,一条直线沿方向v平移,会与另一条直线相交。第一种情况就是,将线段沿方向v平移后交点在两条线段上,那么异面直线的距离就是两条线段的最短距离d。

二是,一中所说的交点不在两条线段上,那么最短距离就是以下4个距离的最小值。

线段1的两个端点到线段2的最短距离、线段2的两个端点到线段1的最短距离。

这里用到的模板就是点到线段的距离。

将距离用分数表示,手动模拟除法就行了。

现在的问题是,上面所说的交点在不在两条线段上,怎么判断呢?

设平面a为线段2与方向v的共平面,

那么如果A、B两点在平面a的同侧,说明交点不会在线段1上,同样的方法判断交点会不会在线段2上。

我的判断是否在平面同侧的方法是,求平面a的法向量n,判断向量CA与向量n的点积是否和向量CB与向量n的点积异号。

因为点积的正负表示向量夹角与90^{o}的关系,如果A、B两点异侧,那么向量CA、CB与向量n的夹角一定是一个大于90^{o},一个小于90^{o}

求法向量用向量叉积即可。

这样所有问题就都解决了。

 

代码: 

#include<bits/stdc++.h>
#define N 10010
#define LL long long
using namespace std;
struct Point
{
    LL x,y,z;
    Point(LL x=0,LL y=0,LL z=0):x(x),y(y),z(z){};
    void read(){cin>>x>>y>>z;}
};

typedef Point Vector;
Vector operator + (Vector a,Vector b){return Vector(a.x+b.x,a.y+b.y,a.z+b.z);}
Vector operator - (Vector a,Vector b){return Vector(a.x-b.x,a.y-b.y,a.z-b.z);}
bool operator == (Vector a,Vector b){return a.x==b.x && a.y==b.y && a.z==b.z;}
LL Dot(Vector a,Vector b){return a.x*b.x+a.y*b.y+a.z*b.z;}  //点积
LL Length(Vector a){return Dot(a,a);}
Vector Cross(Vector a,Vector b) //叉积
{return Vector(a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x);}

LL z[5][2];

void spy(int x,Point p,Point a,Point b)
{
	if(a==b) 
	{
		z[x][0]=Length(p-a);z[x][1]=1;
		return;
	}
    Vector v1=b-a,v2=p-a,v3=p-b;
    if (Dot(v1,v2)<0) {z[x][0]=Length(v2);z[x][1]=1;}else
    if (Dot(v1,v3)>0) {z[x][0]=Length(v3);z[x][1]=1;}else
    {
        z[x][0]=Length(Cross(v1,v2));z[x][1]=Length(v1);
    }
}

void print(LL x,LL y)
{
    int a[40];
    cout<<x/y<<".";
    LL t=x%y;
    for (int i=0;i<30;i++)
    {
        t*=10;
        a[i]=t/y;
        t=t%y;
    }
    if(t*10/y>=5)a[29]++;
    int i=29;
    while(a[i]==10) {a[i]=0;a[--i]++;}
    for (int i=0;i<30;i++)cout<<a[i];cout<<endl;
}

int main()
{
    int T;
    cin>>T;
    Point a,b,aa,bb;
    while(T--)
    {
        a.read();b.read();aa.read();bb.read();
        Vector n=Cross(a-b,aa-bb);
        Vector p1=Cross(b-a,n),p2=Cross(bb-aa,n);
        if (Dot(aa-a,p1)*Dot(bb-a,p1)<0 && Dot(a-aa,p2)*Dot(b-aa,p2)<0)
        {
            Vector v=Cross(a-b,aa-bb);
            if (Length(v)==0)
            {
                cout<<"0.000000000000000000000000000000"<<endl;
            }else
            {
                LL ff=Dot(a-aa,v);
                LL n=Length(v);
                print(ff*ff,n);
            }
        }else
        {
            spy (1,a,aa,bb);
            spy (2,b,aa,bb);
            spy (3,aa,a,b);
            spy (4,bb,a,b);
            for (int i=2;i<=4;i++)
            {
                if (z[1][0]*z[i][1]>z[1][1]*z[i][0])
                {
                    swap(z[1][0],z[i][0]);
                    swap(z[1][1],z[i][1]);
                }
            }
            print(z[1][0],z[1][1]);
        }
    }

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值