【半平面交】[ZJOI2008][HYSBZ\BZOJ1038]瞭望塔
题目链接
分析
看穿题目
考虑组成村庄的每一条线段,显然,我们要在这条线段所在直线上方的半平面内才能看见它。所以,瞭望塔必须要在所有组成村庄的线段的所在直线的上方的半平面的交集内,才能
从瞭望塔的顶端可以看到H村的任意位置
所以,这道题就是求村庄的地面到这个半平面交的最短距离。
实现方式
很多同学看到半平面交就觉得代码一定十分高(e)端(xin)。但是这道题的半平面交十分特殊。
让我们先看看这道例题。
例题
例题链接:【计算几何】[HNOI2008][HYSBZ/BZOJ1007]水平可见直线
例题和这道题中的半平面都是在直线的上方。
例题是求组成半平面交的线段所在的直线是哪些。
回到这道题
具体做法
这道题也可以用同样的方式求出半平面交。
组成半平面交和地面的都是线段,可看做分段函数。
令半平面交的函数为
考虑这f(x),g(x)两个分段函数,每一段都是一次函数,h(x)显然也是一次函数,分段的分界点显然就是f(x),g(x)两个函数的分界点。
而一次函数的极值显然在端点(分界点)处取得。
bingo
我们只需要枚举f(x),g(x)的分界点,然后计算在分界点处的h(x)的值即可,只需要
总时间复杂度
代码
#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);
}