朴素的做法是 O(n2) 枚举点对然后 O(n) 判断这两点连边是否合法。显然会超时,考虑优化判断的过程。
由于数据已经满足相邻两个矩阵必定左右相邻,所以最优路径必定是从
S
点一直朝
在某个点朝右(朝左同理)可以观测到的区域显然是以这个点为顶点的角,而且在向右扩展的过程中,这个角的上半边的变化其实是维护一个上凸包,下半边维护一个下凸包。扩展终止在上半边跑到下半边的下边去的时候。
每次扩展的时候要优先扩展竖直方向的两点,才能保证连边的正确性。
对于同起点的向量
a,b
,若
a,b
的叉积
>0
则说明
a
在
由于边数量的级别是 O(n2) ,所以直接跑 O(n2) 的最短路。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2005;
const double eps=1e-10;
int n,m,ST,EN;
double speed,d[N<<2];
struct P{
double x,y;
P(){}
P(int _x,int _y){x=_x,y=_y;}
P(double _x,double _y){x=_x,y=_y;}
P operator +(P t){return P(x+t.x,y+t.y);}
P operator -(P t){return P(x-t.x,y-t.y);}
}S,T,U[N<<1],D[N<<1];
struct LINE{
P p,v;
LINE(){}
LINE(P _p,P _v){p=_p,v=_v;}
};
struct A{P p[4];}a[N];
double cross(P a,P b){return a.x*b.y-a.y*b.x;}
double dis(P a,P b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
void adde(int x,int y,double w){if(d[x]+w<d[y])d[y]=d[x]+w;}
void link(P s,int st,int l,int r){
P up=P(s.x,s.y+1);
P down=P(s.x,s.y-1);
for(int i=l;i<=r;++i){
if(cross(U[i]-s,up-s)+eps>=0){
up=U[i];
if(cross(up-s,down-s)-eps>=0)return;
adde(st,i*2-1,dis(s,U[i]));
}
if(cross(D[i]-s,down-s)-eps<=0){
down=D[i];
if(cross(up-s,down-s)-eps>=0)return;
adde(st,i*2,dis(s,D[i]));
}
}
}
void link2(){
P up=P(T.x,T.y+1);
P down=P(T.x,T.y-1);
for(int i=m;i;--i){
if(cross(U[i]-T,up-T)-eps<=0){
up=U[i];
if(cross(up-T,down-T)+eps<=0)return;
adde(i*2-1,EN,dis(U[i],T));
}
if(cross(D[i]-T,down-T)+eps>=0){
down=D[i];
if(cross(up-T,down-T)+eps<=0)return;
adde(i*2,EN,dis(D[i],T));
}
}
if(cross(S-T,up)-eps<=0&&cross(S-T,down)+eps>=0)adde(ST,EN,dis(S,T));
}
int main(){
int i,j,x,y,z,x1,y1,x2,y2;
scanf("%d",&n);
for(i=1;i<=n;++i){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
a[i].p[0]=P(x1,y2);
a[i].p[1]=P(x1,y1);
a[i].p[2]=P(x2,y2);
a[i].p[3]=P(x2,y1);
}
scanf("%lf%lf%lf%lf%lf",&S.x,&S.y,&T.x,&T.y,&speed);
if(S.x>T.x)swap(S,T);
for(i=1;i<=n;++i){
if(a[i].p[0].x+eps>=S.x&&a[i].p[0].x-eps<=T.x)U[++m]=a[i].p[0],D[m]=a[i].p[1];
if(a[i].p[2].x+eps>=S.x&&a[i].p[2].x-eps<=T.x)U[++m]=a[i].p[2],D[m]=a[i].p[3];
}
ST=m<<1|1;EN=ST+1;
for(i=1;i<=EN;++i)d[i]=1e9;d[ST]=0;
link(S,ST,1,m);
for(i=1;i<=m;++i){
link(U[i],i*2-1,i,m);
link(D[i],i*2,i,m);
}
link2();
printf("%.8f",d[EN]/speed);
return 0;
}