考虑组成村庄的每一条线段,显然,我们要在这条线段所在直线上方的半平面内才能看见它。所以,瞭望塔必须要在所有组成村庄的线段的所在直线的上方的半平面的交集内,才能
从瞭望塔的顶端可以看到H村的任意位置
所以,这道题就是求村庄的地面到这个半平面交的最短距离。
很多同学看到半平面交就觉得代码一定十分高(e)端(xin)。但是这道题的半平面交十分特殊。
让我们先看看这道例题。
例题链接:【计算几何】[HNOI2008][HYSBZ/BZOJ1007]水平可见直线
例题和这道题中的半平面都是在直线的上方。
例题是求组成半平面交的线段所在的直线是哪些。
这道题也可以用同样的方式求出半平面交。
组成半平面交和地面的都是线段,可看做分段函数。
令半平面交的函数为 f(x) ,地面为 g(x) .
h(x)=f(x)−g(x)
我们只需要枚举f(x),g(x)的分界点,然后计算在分界点处的h(x)的值即可,只需要 O(n) 的时间复杂度。
总时间复杂度 O(nlogn) (排序的时间复杂度)
#include<cstdio>
#include<algorithm>
#include<cmath>
#define MAXN 300
using namespace std;
#define EPS 1e-9
#define INF 1e27
struct node{
double k,b;
bool operator<(const node &x)const{
if(fabs(k-x.k)<EPS)
return b>x.b;
return k<x.k;
}
bool operator==(const node &x)const{
return fabs(k-x.k)<EPS;
}
}a[MAXN+10],b[MAXN+10];
struct point{
double k,b,x,y;
point(){
};
point(node a,double xx,double yy){
x=xx,y=yy,k=a.k,b=a.b;
}
}p[MAXN+10];
int n,x[MAXN+10],y[MAXN+10],na,np;
double ans=INF;
void Read(int &x){
char c;
bool f=0;
while(c=getchar(),c!=EOF){
if(x=='-')
f=1;
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
if(f)
x=-x;
return;
}
}
}
void read(){
int i;
Read(n);
for(i=1;i<=n;i++)
Read(x[i]);
for(i=1;i<=n;i++)
Read(y[i]);
for(i=1;i<n;i++){
b[i].k=1.0*(y[i]-y[i+1])/(x[i]-x[i+1]);
b[i].b=y[i]-x[i]*b[i].k;
a[i]=b[i];
}
sort(a+1,a+n);
na=unique(a+1,a+n)-a-1;
}
void ints_halfplane(){
int i,j,ti;
double tx,x;
for(i=1;i<na;){
x=INF;
for(j=i+1;j<=na;j++){
tx=(a[j].b-a[i].b)/(a[i].k-a[j].k);
if(tx<=x)
ti=j,x=tx;
}
p[++np]=point(a[i],x,x*a[i].k+a[i].b);
i=ti;
}
p[++np]=point(a[na],INF,0);
}
void solve(){
int i,j;
ints_halfplane();
for(i=j=1;i<=np&&j<=n;){
if(p[i].x<x[j]){
ans=min(p[i].y-b[j-1].k*p[i].x-b[j-1].b,ans);
i++;
}
else{
ans=min(p[i].k*x[j]+p[i].b-y[j],ans);
j++;
}
}
}
int main()
{
read();
solve();
printf("%.3lf",ans);
}