UVA 11796 Dog Distance(点与向量)
题意:
甲和乙两条狗分别沿着两条折线各自奔跑, 他们同时出发,且同时到达终点. 现在要你求他们之间的最远距离与最近距离之差.
分析:
刘汝佳<<训练指南>>P261例题.
首先我们想一下如果甲乙都仅沿着两条线段向前跑,那么他们之间的最短和最长距离怎么算? 假设甲的速度向量为v1(速度向量指甲单位时间所走的位移向量),乙的速度向量为v2. 那么可以把甲看成是静止不动的,而让乙一个人以v2-v1的速度向量去运动(画图验证下).
且假设甲和乙都运动了t秒时间,那么我们可以知道上面的过程类似于甲不动,乙从自己的起点运动了t*(v2-v1)的位移向量. 而乙已知起点和位移向量,那么它就是在一条线段上. 最终我们要求的就是甲这个不动的点到乙这条线段的最大和最小距离即可.(仔细体会下)
下面我们回到原问题. 原问题是一段一段的线段连接起来的折线,但是在某一段时刻,甲乙必然都是同时在走直线段的.
我们只要把握住甲乙的当前点和下一个拐点这两个信息即可.
假设当前甲在Pa点,乙在Pb点.而他们的下一个拐点是Sa和Sb,(由于总距离已知且他们花的时间相同,所以他们的速度va和vb已知).
所以我们可以知道到底甲先到拐点还是乙先到拐点,那么他们在到达拐点的这段时间内就都是走的直线段. 然后我们处理这段的最大最小值.接着更新他们的当前位置点和下一个拐点即可接着循环处理,一直到终点.
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps=1e-10;
int dcmp(double x)
{
if(fabs(x)<eps) return 0;
return x<0?-1:1;
}
struct Point
{
double x,y;
Point(){}
Point(double x,double y):x(x),y(y){}
bool operator ==(Point &B)
{
return dcmp(x-B.x)==0 && dcmp(y-B.y)==0;
}
};
typedef Point Vector;
Vector operator+(Vector A,Vector B)
{
return Vector(A.x+B.x,A.y+B.y);
}
Vector operator-(Point A,Point B)
{
return Vector(A.x-B.x,A.y-B.y);
}
Vector operator*(Vector A,double p)
{
return Vector(A.x*p, A.y*p);
}
Vector operator/(Vector A,double p)
{
return Vector(A.x/p, A.y/p);
}
double Dot(Vector A,Vector B)
{
return A.x*B.x+A.y*B.y;
}
double Length(Vector A)
{
return sqrt(Dot(A,A));
}
double Cross(Vector A,Vector B)
{
return A.x*B.y-A.y*B.x;
}
double DistanceToLine(Point P,Point A,Point B)
{
return fabs(Cross(P-A,P-B))/Length(B-A);
}
double DistanceToSegment(Point P,Point A,Point B)
{
if(A==B) return Length(P-A);
Vector v1=P-A,v2=P-B,v3=B-A;
if(Dot(v1,v3)<0) return Length(v1);
else if(Dot(v2,v3)>0) return Length(v2);
else return fabs(Cross(v1,v3))/Length(v3);
}
/***以上为刘汝佳模板部分***/
const int maxn=60+5;
int T,A,B;
Point P[maxn],Q[maxn];
double min_v,max_v;
void Update(Point P,Point A,Point B)//P点到线段AB的最大最小距离
{
min_v=min(min_v, DistanceToSegment(P,A,B));
max_v=max(max_v, max(Length(P-A), Length(P-B) ) );
}
int main()
{
int T; scanf("%d",&T);
for(int kase=1;kase<=T;++kase)
{
scanf("%d%d",&A,&B);
for(int i=0;i<A;++i) scanf("%lf%lf",&P[i].x,&P[i].y);
for(int i=0;i<B;++i) scanf("%lf%lf",&Q[i].x,&Q[i].y);
double LenA=0,LenB=0;//即是两条折线的总长度,(假设甲乙都只运动1秒)也是他们的速度
for(int i=0;i<A-1;++i) LenA += Length(P[i+1]-P[i]);
for(int i=0;i<B-1;++i) LenB += Length(Q[i+1]-Q[i]);
min_v=1e9, max_v=-1e9;//或 min_v=max_v=Length(P[0]-Q[0]);
int Sa=1,Sb=1; //甲乙下一个拐点的编号
Point Pa=P[0],Pb=Q[0]; //甲乙当前位置点
while(Sa<A && Sb<B)
{
double La=Length(P[Sa]-Pa);
double Lb=Length(Q[Sb]-Pb);
double t=min(La/LenA,Lb/LenB);//甲乙到下一个拐点的最小时间
Vector Va=(P[Sa]-Pa)/La*(t*LenA);
Vector Vb=(Q[Sb]-Pb)/Lb*(t*LenB);
Update(Pa,Pb,Pb+Vb-Va);
Pa = Pa+Va;
Pb = Pb+Vb;
if(Pa==P[Sa]) ++Sa;
if(Pb==Q[Sb]) ++Sb;
}
printf("Case %d: %.0lf\n",kase,max_v-min_v);
}
return 0;
}