[HDU3151]Cave Crisis
XXX.[HDU3151]Cave Crisis
一眼看上去跟XII.[ABC181F]Silver Woods完全一致,因此考虑一样的思路。
于是我们现在问题变为求出两个多边形间的距离。
首先先考虑如何判断它们是否有交。有交只有一种可能,就是边有交。于是我们枚举两个多边形所有的边,然后判断它们是否有交。具体而言,我们求出两条边所在直线的交点,然后用点积判断该交点是否都在两条线段上即可。
但是我一开始没想通还写了个判断点是否在另一个多边形内部的代码,但显然此种情形是可以被前一种完美包含的。但是这确实有用——这让我不必去判断两条线段是否平行,因为平行且相交只能是有一条线段其中某个点在另一条线段上,也即在另一个多边形的边界上,但是此种情形我已经在内部判断过了(这里的内部包含边界)。
然后就是求距离了。距离只有两种可能,点到边,点到点。点到点很好算,直接 \(n^2\) 枚举即可,关键是点到边。我采取的方式是作出点到边的垂线,然后计算垂线与原直线的交点,然后判断该交点是否在线段上,不是则计算到两线段端点距离的较小值,是则计算到该交点的距离。
搞完之后排个序冰茶姬维护就行了。
但需要注意的是,上一题的起点是 \((-\infty,0)\),但是本题的起点却是 \((0,0)\)!这意味着在起点处就不能拥有太大的半径,最终答案需要与起点到所有多边形的最小距离取 \(\min\)。
但又一个坑点出现了:该最小距离是半径,但是我们上文计算出的两多边形间距离却是直径!
千疮百孔的代码:
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-12;
int m,n;
int cmp(double x){
if(x>eps)return 1;
if(x<-eps)return -1;
return 0;
}
struct Vector{
double x,y;
Vector(){}
Vector(double X,double Y){x=X,y=Y;}
friend Vector operator +(const Vector &u,const Vector &v){return Vector(u.x+v.x,u.y+v.y);}
friend Vector operator -(const Vector &u,const Vector &v){return Vector(u.x-v.x,u.y-v.y);}
friend Vector operator *(const Vector &u,const double &v){return Vector(u.x*v,u.y*v);}
friend Vector operator /(const Vector &u,const double &v){return Vector(u.x/v,u.y/v);}
friend double operator &(const Vector &u,const Vector &v){return u.x*v.y-u.y*v.x;}//cross times
friend double operator |(const Vector &u,const Vector &v){return u.x*v.x+u.y*v.y;}//point times
double operator ~()const{return sqrt(x*x+y*y);}//the modulo of a vector
double operator !()const{return atan2(y,x);}//the angle of a vector
void read(){scanf("%lf%lf",&x,&y);}
void print()const{printf("(%lf,%lf)",x,y);}
};
typedef Vector Point;
struct Line{
Point x,y;
Vector z;
Line(){}
Line(Point X,Point Y){x=X,y=Y,z=Y-X;}
friend Point operator &(const Line &u,const Line &v){return u.x+u.z*((v.z&(u.x-v.x))/(u.z&v.z));}
friend double operator-(const Line &u,const Point &v){//calculate the distance between u and v
Line w(v,Point(v.x+u.z.y,v.y-u.z.x));
Point o=(u&w);
if(cmp((o-u.x)|(o-u.y))!=1)return ~(o-v);
return min(~(u.x-v),~(u.y-v));
}
};
typedef Line Segment;
vector<Point>v[110];
struct edge{
int u,v;
double w;
edge(int U,int V,double W){u=U,v=V,w=W;}
friend bool operator<(const edge&u,const edge&v){return u.w<v.w;}
};
vector<edge>u;
int dsu[110];
int find(int x){return dsu[x]==x?x:dsu[x]=find(dsu[x]);}
void merge(int x,int y){
x=find(x),y=find(y);
if(x==y)return;
dsu[y]=x;
}
bool same(int x,int y){return find(x)==find(y);}
int main(){
while(true){
scanf("%d%d",&m,&n);
if(!m&&!n)return 0;
for(int i=1,z;i<=n;i++){
v[i].clear();
Vector V;
scanf("%d",&z);
while(z--)V.read(),v[i].push_back(V);
}
u.clear();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j)continue;
double dis=0x3f3f3f3f;
for(auto I:v[i]){
bool in=true;//if a point is inside the other graph
for(int J=0;J<v[j].size();J++)if(cmp((v[j][J]-I)&(v[j][(J+1)%v[j].size()]-I))==-1){in=false;break;}
if(in){dis=-1;break;}
for(int J=0;J<v[j].size();J++)dis=min(dis,Line(v[j][J],v[j][(J+1)%v[j].size()])-I);
}
for(int I=0;cmp(dis)==1&&I<v[i].size();I++)for(int J=0;J<v[j].size();J++){//if two segments intersect
Vector i1=v[i][I],i2=v[i][(I+1)%v[i].size()];
Vector j1=v[j][J],j2=v[j][(J+1)%v[j].size()];
if(cmp((i1-i2)&(j1-j2))==0)continue;//two segments parallel
Vector k=Line(i1,i2)&Line(j1,j2);
// i1.print(),i2.print(),j1.print(),j2.print(),k.print();puts("");
if(cmp((i1-k)|(i2-k))!=1&&cmp((j1-k)|(j2-k))!=1){dis=-1;break;}
}
u.emplace_back(i,j,dis);
}
double mx=-0x3f3f3f3f,mn=0x3f3f3f3f;
for(auto I:v[i])mx=max(mx,I.y),mn=min(mn,I.y);
u.emplace_back(n+1,i,m/2.0-mx),u.emplace_back(n+2,i,mn+m/2.0);
}
u.emplace_back(n+1,n+2,m);
for(int i=1;i<=n+2;i++)dsu[i]=i;
sort(u.begin(),u.end());
// for(auto i:u)printf("%d %d %lf\n",i.u,i.v,i.w);
double ans=0x3f3f3f3f;
for(int i=1;i<=n;i++)for(int I=0;I<v[i].size();I++)ans=min(ans,Line(v[i][I],v[i][(I+1)%v[i].size()])-Point(0,0));
ans*=2;
for(int i=0;i<u.size();i++){
merge(u[i].u,u[i].v);
if(same(n+1,n+2)){if(cmp(min(ans,u[i].w))!=1)puts("impossible");else printf("%.2lf\n",min(ans,u[i].w)/2);break;}
}
}
return 0;
}