题意
有一个无限大的网格图,要求从(x1,y1)走到(x2,y2)。有n个格点上有半径为0.1个单位长度的喷泉,经过喷泉时必须沿着圆周走。问最短路。
n<=200000,x1,y1,x2,y2<=1e8,满足没有两个喷泉在同一行或同一列。
分析
不难发现走喷泉的话,拐弯会使得路径缩短,直走会使得路径增长。
贪心地想,肯定是要尽量拐弯且走的喷泉越多越好。
因为喷泉半径实在太小,所以不能绕弯。
不难发现这就是一个最长上升子序列问题。
还有一个情况就是若每行或每列都要走喷泉的话则必须有一个喷泉要直走。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define y1 yy1
#define y2 yy2
using namespace std;
const int N=200005;
const int inf=1e8;
const double pi=acos(-1);
int n,x1,x2,y2,y1,f[N],g[N],b[N];
struct data{int x,y;}a[N];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
bool cmp(data a,data b)
{
return a.x<b.x;
}
int main()
{
x1=read();y1=read();x2=read();y2=read();
n=read();
for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read();
if (x1>x2)
{
x1=inf-x1;x2=inf-x2;
for (int i=1;i<=n;i++) a[i].x=inf-a[i].x;
}
if (y1>y2)
{
y1=inf-y1;y2=inf-y2;
for (int i=1;i<=n;i++) a[i].y=inf-a[i].y;
}
sort(a+1,a+n+1,cmp);
int tot=0;
for (int i=1;i<=n;i++)
if (a[i].x>=x1&&a[i].x<=x2&&a[i].y>=y1&&a[i].y<=y2) b[++tot]=a[i].y;
int mx=0;
for (int i=1;i<=tot;i++)
{
f[i]=lower_bound(g+1,g+mx+1,b[i])-g;
if (f[i]>mx) mx=f[i],g[mx]=b[i];
else g[f[i]]=min(g[f[i]],b[i]);
}
double ans=(double)(x2+y2-x1-y1)*100;
ans-=mx*(20-5*pi);
if (mx==min(y2-y1+1,x2-x1+1)) ans+=5*pi;
printf("%.12lf",ans);
return 0;
}