bzoj 1020 安全的航线|计算几何

一道计算几何的神题,复(xue)习了各种计算几何的基础知识
先上题解
将航线上的每一段线段加入循环队列
对于每一个线段l,r,求出到l最近的点p1和到r最近的点p2.在线段l,r内找出和p1,p2长度相等的点p,设所有陆地与p的最近点为p3,那么线段[l,p]和[p,r]的答案不会超过max(len(l,p1),len(p,p3),len(r,p2)),也就是如果这个值小于当前答案的话,线段上任意一点不会更新答案,否则将[l,p],[p,r]加入循环队列
注意,如果达到精度要求就不用继续计算了,否则会死循环。
复习了以下知识点:
点积,叉积,向量的模长
点在线上的判定 33' bool on
线段相交的判定 39' bool inter
点在多边形内的判定 48' bool in
点到线段的距离 61' double dis
点到线段最近点的求法 70' getit
总之,这还是一道计算几何的好题

 

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>

 

#define md
#define ll long long
#define inf (int) 1e9
#define eps 1e-8
#define N
using namespace std;
struct point { double x,y;} xian[25];
struct line {point x,y;} q[1000010];
struct near {point x;double dis;};
const int maxn=1000000;
int n,m;
double ans=0;
inline double sqr(double a) { return a*a;}
int fcmp(double a,double b)
{
 if (a-b>eps) return 1;
 if (a-b<-eps) return -1;
 return 0;
}
double dot(point a,point b) { return a.x*b.x+a.y*b.y;}
bool operator ==(point a,point b) { point p=(point){a.x-b.x,a.y-b.y}; return fcmp(p.x,0)==0&&fcmp(p.y,0)==0;}
double operator * (point a,point b) { return a.x*b.y-a.y*b.x;}
point operator * (point a,double b) { return (point){a.x*b,a.y*b};}
point operator / (point a,double b) { return (point) {a.x/b,a.y/b};}
point operator - (point a,point b)  { return (point){a.x-b.x,a.y-b.y};}
point operator + (point a,point b) { return (point) {a.x+b.x,a.y+b.y};}
inline double len(point a) { return sqrt(a.x*a.x+a.y*a.y);}
bool on (point a,point b,point c)
{
 if (a==b||a==c) return 1;
 return (fcmp((a-b)*(c-b),0)==0&&fcmp(dot(a-b,a-c),0)<0);//
}
bool inter(point a,point b,point c,point d)
{
 return fcmp(((b-a)*(c-a))*((b-a)*(d-a)),0)<0&&fcmp(((d-c)*(a-c))*((d-c)*(b-c)),0)<0;
}
 
struct LAND
{
 int tot;
 point a[35];
 bool in(point x)
 {
  for (int i=1;i<=tot;i++)
    if (on(x,a[i],a[i+1])) return 1;
  x.y+=0.1;
  point p=(point){-10001,x.y+0.1};
  int jiao=0;
  for (int i=1;i<=tot;i++) jiao+=inter(p,x,a[i],a[i+1]);
  x.y-=0.1;
  return jiao&1;
 }
}land[25];
   
inline double dis(point a,line b)
{
 
 point p1=a-b.x,p2=b.y-b.x,p3=a-b.y,p4=b.x-b.y;
 if (b.x==b.y) return len(p1);
 if (fcmp(dot(p1,p2),0)<0) return len(p1);
 if (fcmp(dot(p3,p4),0)<0) return len(p3);
 return (p1*p2/len(p2));
}
inline near getit(point a,point b,point c)
{
 point p1=a-b,p2=c-b,p3=a-c,p4=b-c;
 if (b==c) return (near){b,len(p1)};
 if (fcmp(dot(p1,p2),0)<=0) return (near){b,len(p1)};
 if (fcmp(dot(p3,p4),0)<=0) return (near){c,len(p3)};
 double t=dot(p1,p2)/dot(p2,p2);
 point d=b+p2*t; return (near){d,len(d-a)};

 

near find(point x)
{
 for (int i=1;i<=n;i++) if (land[i].in(x)) return (near){x,0};
 double mlen=inf;
 near xans=(near){x,inf};
 for (int i=1;i<=n;i++)
 {
  for (int j=1;j<=land[i].tot;j++)
  {
   near now=getit(x,land[i].a[j],land[i].a[j+1]);
   if (fcmp(now.dis,xans.dis)<=0) xans=now;
  }
 }
 ans=max(ans,xans.dis);
 return xans;
}

 

void solve()
{
 ans=0;
 int h=0,w=0;
 for (int i=1;i<m;i++)
  q[++w]=(line){xian[i],xian[i+1]};
 for (int i=1;i<=m;i++) find(xian[i]);
 while (h!=w)
 {
  h++; if (h>maxn+2) h=1;
  line L=q[h];
  near p1=find(L.x),p2=find(L.y);
  point l=L.x,r=L.y;
  while (len(l-r)>(1e-4))
  {
   point mid=(l+r)/2;
   int bo=fcmp(len(mid-p1.x),len(mid-p2.x));
   if (bo==1) r=mid;
   else if (bo==0) l=r=mid;
   else l=mid;
  }
  double nowans=max(len(l-p1.x),len(l-p2.x));
  near p=find(l);
  if (ans+0.004<nowans)
  {
   line L1=(line){L.x,l},L2=(line){l,L.y};
   w++; if (w>maxn+2) w=1; q[w]=L1;
   w++; if (w>maxn+2) w=1; q[w]=L2;
  }
 }
}

 

point read() { point a; scanf("%lf%lf",&a.x,&a.y); return a;}  

int main()
{
 //freopen("1020.in","r",stdin); freopen("1020.out","w",stdout);
 scanf("%d%d",&n,&m);
 for (int i=1;i<=m;i++) xian[i]=read();
 for (int i=1;i<=n;i++)
 {
  scanf("%d",&land[i].tot);
  for (int j=1;j<=land[i].tot;j++) land[i].a[j]=read();
  land[i].a[land[i].tot+1]=land[i].a[1];//?
 }
 solve();
 printf("%.2lf\n",ans);
 return 0;
}

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值