# 【BZOJ 1020】 [SHOI2008]安全的航线flight

11 篇文章 0 订阅

### 1020: [SHOI2008]安全的航线flight

Time Limit: 1 Sec   Memory Limit: 162 MB
Submit: 754   Solved: 243
[ Submit][ Status]

1 2
-9 -6
5 1
3
0 16
-16 -12
17 -6

0.00

### Source

1.首先把航线上的所有线段加入队列

2.对于每一条线段

（1）我们首先找到线段左右端点的陆地最近点为p1,p2

（2）用二分法找到这条线段上与p1,p2几乎等距的点x，且这个距离为d

（3）那么整条线段上的点到最近陆地的距离一定不会超过d。  为什么呢？

首先x到最近陆地的距离一定<=d；然后如果从x向左端点走，那么离p1越来越近，到p1的距离<d，就有了保底了；对于向右走同理。

（4）如果这个d小于等于当前的ans,那么整条线段可以舍弃了；否则把左端点到x的线段以及x到右端点的线段都加入队列

3.当队列为空时算法结束

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#define eps 1e-16
#define MQ 100000
#define M 50
using namespace std;
int n,m;
double ans;
int dcmp(double x)
{
if (fabs(x)<eps) return 0;
return x>eps?1:-1;
}
struct Point
{
double x,y;
Point() {}
Point(double x,double y): x(x),y(y) {}
{
scanf("%lf %lf",&x,&y);
}
friend Point operator + (Point a,Point b)
{
return Point(a.x+b.x,a.y+b.y);
}
friend Point operator - (Point a,Point b)
{
return Point(a.x-b.x,a.y-b.y);
}
friend Point operator * (Point a,double p)
{
return Point(a.x*p,a.y*p);
}
friend Point operator / (Point a,double p)
{
return Point(a.x/p,a.y/p);
}
friend bool operator == (Point a,Point b)
{
return !dcmp(a.x-b.x)&&!dcmp(a.y-b.y);
}
}flight[M];
typedef Point Vector;
double Dot(Vector a,Vector b)
{
return a.x*b.x+a.y*b.y;
}
double Len(Vector a)
{
return sqrt(Dot(a,a));
}
double Cross(Vector a,Vector b)
{
return a.x*b.y-a.y*b.x;
}
bool On(Point a,Point b,Point c)
{
return !dcmp(Cross(b-a,c-a))&&dcmp((a.x-b.x)*(a.x-c.x))<=0&&dcmp((a.y-b.y)*(a.y-c.y))<=0;
}
bool inter(Point a,Point b,Point c,Point d)
{
return dcmp(Cross(b-a,c-a)*Cross(b-a,d-a))<=0&&dcmp(Cross(d-c,a-c)*Cross(d-c,b-c))<=0;
}
Vector Normal(Vector a)
{
return Vector(-a.y,a.x);
}
struct Seg
{
Point a,b;
Seg() {}
Seg(Point a,Point b):a(a),b(b) {}
}queue[1000000+5];
struct Polygon
{
Point p[M];
int tot;
bool In(Point &point)
{
int total=0;
for (int i=1;i<=tot;i++)
if (On(point,p[i],p[i%tot+1]))
return true;
Point ray=Point(-10001,point.y);
for (int i=1;i<=tot;i++)
total+=inter(ray,point,p[i],p[i%tot+1]);
}
}island[M];
struct near
{
Point P;
double dis;
near() {}
near(Point a,double b): P(a),dis(b) {}
};
void init()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
for (int i=1;i<=n;i++)
{
scanf("%d",&island[i].tot);
for (int j=1;j<=island[i].tot;j++)
}
}
bool check(Point p)
{
for (int i=1;i<=n;i++)
if (island[i].In(p))
return true;
return false;
}
Point Getinter(Point a,Vector b,Point c,Vector d) //求垂足
{
Vector u=a-c;
double t=Cross(d,u)/Cross(b,d);
return a+b*t;
}
near DISPS(Point a,Point b,Point c)
{
if (b==c) return near(b,Len(b-a));
Vector v1=c-b,v2=a-b,v3=a-c;
if (dcmp(Dot(v1,v2)<=0)) return near(b,Len(v2));
if (dcmp(Dot(v1,v3)>=0)) return near(c,Len(v3));
Vector v=Normal(b-c);      //求与bc垂直的向量
Point ans=Getinter(a,v,b,v1);
return near(ans,Len(a-ans));
}
near Find(Point &p)  //找最近点
{
if (check(p)) return near(p,0);
near ans1;
ans1.dis=1<<30;
for (int i=1;i<=n;i++)
for (int j=1;j<=island[i].tot;j++)
{
near get=DISPS(p,island[i].p[j],island[i].p[j%island[i].tot+1]);
if (dcmp(ans1.dis-get.dis)>=0) ans1=get;
}
ans=max(ans,ans1.dis);
return ans1;
}
void Solve()
{
int front=0,rear=0;
for (int i=1;i<m;i++)
queue[++rear]=Seg(flight[i],flight[i+1]),Find(flight[i]);
Find(flight[m]);
while (front!=rear)
{
while (Len(r-l)>1e-4)
{
Point mid=(l+r)/2;
if (Len(mid-p1)<Len(mid-p2)) l=mid;
else r=mid;
}
double nowans=min(Len(l-p1),Len(l-p2));
Find(l);
if (ans+0.005<nowans)
}
}
int main()
{
init();
Solve();
printf("%.2lf\n",ans);
return 0;
}


1.代码基本抄自ydc，略有改动。

near() {}
near(Point a,double b): P(a),dis(b) {}

2.这道题中求与p1,p2等距的点的过程相当于一个剪枝过程

• 0
点赞
• 0
收藏
觉得还不错? 一键收藏
• 0
评论
07-07 96
02-25 121
03-02 1235
12-11 1766
03-11 719
08-17 390
04-20 1650
01-30 1296
11-16 633
09-26 1万+

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