http://www.lydsy.com/JudgeOnline/problem.php?id=2433
一个很显然的 O(n3) 做法:每个矩形上有四个顶点,首先暴力枚举定点对 (i,j) ,再枚举其他的点,叉积判断是否有矩形与矩形的连接处卡住了直线 ij ,若没有被卡,那么在图中连 i 到 j 的无向边,边权为两点间的距离。然后随便用个什么最短路算法求出起点到终点的最短路就可以得到答案。
这个方法非常丽洁,可惜过不了这道题。上面的做法中判断直线 i,j 是否被卡,很多时候是在做重复劳动,更好的做法是用DP,用 f[i] 表示从起点 S 到点 i 的最短距离,可以得到下面的一个DP方程
但是要加上一个特判,如下面的情况
用红色的点去更新蓝色的点的DP值,然后直接退出。因为对于红色的点右边矩形中的所有点(蓝色点除外),与红色点的连线都是不合法的,因此后面就没有必要更新了,可以直接退出。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cmath>
#define MAXE 10000
#define MAXV 51000
using namespace std;
int n;
double f[MAXV];
struct Point
{
int x,y;
Point(){}
Point(int _x,int _y):x(_x),y(_y){}
}points[MAXV],S,T;
Point operator-(Point a,Point b)
{
return Point(a.x-b.x,a.y-b.y);
}
int cross(Point a,Point b)
{
return a.x*b.y-a.y*b.x;
}
double dist(Point a,Point b)
{
return sqrt((double)((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
bool inRange(Point a,Point up,Point dn,Point d) //判断d是否在a->b,a->c围成的夹角里
{
if(cross(up-a,d-a)>0||cross(dn-a,d-a)<0) return false;
return true;
}
struct Square
{
Point zx,zs,yx,ys;
Square(){}
Square(Point zx,Point zs,Point yx,Point ys):zx(zx),zs(zs),yx(yx),ys(ys){}
}sqr[MAXV];
bool inSquare(Square a,Point b) //判断点b是否在矩形a内
{
return b.x>=a.zx.x&&b.y>=a.zx.y&&b.x<=a.ys.x&&b.y<=a.ys.y;
}
double ans=1e20;
void update(Point p,int pos,double val) //用矩形pos的点p的f值val去更新这个矩形后面的矩形的每个点的f值
{
Point up=Point(p.x,p.y+1);
Point dn=Point(p.x,p.y-1);
for(int i=pos;i<=n;i++)
{
if(inRange(p,up,dn,sqr[i].zx))
f[i*4]=min(f[i*4],val+dist(p,sqr[i].zx));
if(inRange(p,up,dn,sqr[i].zs))
f[i*4+1]=min(f[i*4+1],val+dist(p,sqr[i].zs));
if(inRange(p,up,dn,sqr[i].yx))
f[i*4+2]=min(f[i*4+2],val+dist(p,sqr[i].yx));
if(inRange(p,up,dn,sqr[i].ys))
f[i*4+3]=min(f[i*4+3],val+dist(p,sqr[i].ys));
if(inSquare(sqr[i],T)&&inRange(p,up,dn,T))
ans=min(ans,val+dist(p,T));
if(i+1<=n) //更新三角区域范围
{
Point lowerBound=Point(sqr[i].yx.x,max(sqr[i].yx.y,sqr[i+1].zx.y));
Point upperBound=Point(sqr[i].yx.x,min(sqr[i].ys.y,sqr[i+1].zs.y)); //矩形i与i+1相连部分是一条竖着的直线lowerBound-upperBound
if(p.x==sqr[i].yx.x)
{
if(lowerBound.y>p.y||upperBound.y<p.y)
{
f[(i+1)*4]=min(f[(i+1)*4],val+dist(p,sqr[i+1].zx));
f[(i+1)*4+1]=min(f[(i+1)*4+1],val+dist(p,sqr[i+1].zs));
return;
}
}
else //点p是在当前矩形的左侧,那么就要用矩形i与i+1的交界线段去更新三角形的可行区域
{
if(cross(dn-p,lowerBound-p)>0) dn=lowerBound;
if(cross(up-p,upperBound-p)<0) up=upperBound;
if(cross(up-p,dn-p)>0) return; //已经没有可行区域了
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int zxx,zxy,ysx,ysy;
scanf("%d%d%d%d",&zxx,&zxy,&ysx,&ysy);
sqr[i].zx=Point(zxx,zxy);
sqr[i].zs=Point(zxx,ysy);
sqr[i].yx=Point(ysx,zxy);
sqr[i].ys=Point(ysx,ysy);
}
scanf("%d%d",&S.x,&S.y);
scanf("%d%d",&T.x,&T.y);
if(S.x>T.x) swap(S,T);
for(int i=0;i<=4*n+4;i++) f[i]=1e20;
for(int i=1;i<=n;i++) //!!!!!
{
if(inSquare(sqr[i],S)) update(S,i,0);
update(sqr[i].zx,i,f[i*4]);
update(sqr[i].zs,i,f[i*4+1]);
update(sqr[i].yx,i,f[i*4+2]);
update(sqr[i].ys,i,f[i*4+3]);
}
double speed;
scanf("%lf",&speed);
printf("%lf\n",ans/speed);
return 0;
}