[bzoj1020][SHOI2008]安全的航线flight【迭代】【计算几何】
【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=1020
【题解】
首先有一个朴素的想法就是二分,然后判断带圆弧的多边形是否能完全覆盖直线,是不是很简单?
但我不会 QAQ
考虑暴力枚举每一个点(隔0.005),求出它到最近陆地的距离,取最大值。
这样做编程复杂度会下降很多,但会 TLE。
观察后发现,许多状态是多余的,于是想到剪枝,具体的方法是:
1.将每一条线段加入队列中。
2.区出队收线段,找出线段两端的最近点,二分找出距离这两个点最远的在线段上点,设为 mid,若它到这两个点的最近距离 ≤ 当前答案,就将整个线段删除。
tips:找 mid 时,它要么在线段最左边,要么在最右边,要么在垂直平分线上,所以二分垂直平分线(直接求也行),然后与左右比较。
3.用 mid 更新答案。
4.加入左端点到 mid,mid 到右端点的线段。(满足长度≥0.005)
5.重复2-4直到队列为空。
这种做法能顺利通过此题,复杂度 O(???)
还是挺好(nan)写的
/* --------------
user Vanisher
problem bzoj-1020
----------------*/
# include <bits/stdc++.h>
# define ll long long
# define inf 1e9
# define eps 1e-4
# define anseps 1e-3
# define N 110
# define T 5000000
using namespace std;
int num[N],n,m,k;
struct point{
double x,y;
}mp[N][N],rd[N];
struct seg{
point a,b;
}l[N*N],q[T];
double ans;
point operator +(point x, point y){return (point){x.x+y.x,x.y+y.y};}
point operator -(point x, point y){return (point){x.x-y.x,x.y-y.y};}
point operator *(point x, double y){return (point){x.x*y,x.y*y};}
point operator /(point x, double y){return (point){x.x/y,x.y/y};}
double sqr(double x){return x*x;}
double dis(point x, point y){
return sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));
}
double dot(point x, point y){
return x.x*y.x+x.y*y.y;
}
double cross(point x, point y){
return x.x*y.y-x.y*y.x;
}
double dis_PtoS(point x, seg y){
if (dot(x-y.a,y.b-y.a)<0) return dis(x,y.a);
if (dot(x-y.b,y.a-y.b)<0) return dis(x,y.b);
return fabs(cross(y.a-x,y.b-x))/dis(y.a,y.b);
}
point PtoS(point x, seg y){
if (dot(x-y.a,y.b-y.a)<0) return y.a;
if (dot(x-y.b,y.a-y.b)<0) return y.b;
double d=dis_PtoS(x,y), d1=sqrt(sqr(dis(y.a,x))-sqr(d));
return y.a+(y.b-y.a)*(d1/dis(y.a,y.b));
}
bool PonS(point x, seg y){
return fabs(cross(x-y.a,x-y.b))<eps&&dot(x-y.a,x-y.b)<eps;
}
bool ScrossS(seg a, seg b){
return cross(a.a-b.a,b.b-b.a)*cross(a.b-b.a,b.b-b.a)<eps&&cross(b.a-a.a,a.b-a.a)*cross(b.b-a.a,a.b-a.a)<eps;
}
bool in(point x, int id){
for (int i=1; i<=num[id]; i++)
if (PonS(x,(seg){mp[id][i],mp[id][i+1]})==true)
return true;
point y={10001,x.y+0.1}; int cnt=0;
for (int i=1; i<=num[id]; i++)
if (ScrossS((seg){x,y},(seg){mp[id][i],mp[id][i+1]})==true) cnt++;
return cnt%2;
}
int read(){
int tmp=0, fh=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
return tmp*fh;
}
bool check(point x){
bool now=false;
for (int i=1; i<=m; i++){
now=now|in(x,i);
}
return now;
}
point findnear(point x){
if (check(x)) return x;
point mn={inf,inf};
for (int i=1; i<=k; i++){
point now=PtoS(x,l[i]);
if (dis(mn,x)>dis(now,x)) mn=now;
}
return mn;
}
void checkans(point x){
point y=findnear(x);
ans=max(ans,dis(x,y));
}
int main(){
m=read(), n=read();
for (int i=1; i<=n; i++)
rd[i].x=read(), rd[i].y=read();
for (int i=1; i<=m; i++){
num[i]=read();
for (int j=1; j<=num[i]; j++)
mp[i][j].x=read(), mp[i][j].y=read();
mp[i][num[i]+1]=mp[i][1];
for (int j=1; j<num[i]; j++)
l[++k].a=mp[i][j], l[k].b=mp[i][j+1];
l[++k].a=mp[i][num[i]], l[k].b=mp[i][1];
}
int pl=1, pr=0;
for (int i=1; i<n; i++)
q[++pr].a=rd[i],q[pr].b=rd[i+1];
while (pl<=pr){
seg now=q[pl++];
point lp=findnear(now.a), rp=findnear(now.b), l=now.a, r=now.b, mid;
while (dis(l,r)>anseps){
mid=(l+r)/2;
if (dis(mid,lp)>dis(mid,rp))
r=mid; else l=mid;
}
checkans(now.a); checkans(now.b); checkans(mid);
if (dis(mid,lp)<ans+eps) continue;
if (dis(now.a,mid)>anseps) q[++pr].a=now.a, q[pr].b=mid;
if (dis(mid,now.b)>anseps) q[++pr].a=mid, q[pr].b=now.b;
}
printf("%.2lf\n",ans);
return 0;
}