題目連結
分析
看穿題目
考慮組成村莊的每一條線段,顯然,我們要在這條線段所在直線上方的半平面內才能看見它。所以,瞭望塔必須要在所有組成村莊的線段的所在直線的上方的半平面的交集內,才能
從瞭望塔的頂端可以看到H村的任意位置
所以,這道題就是求村莊的地面到這個半平面交的最短距離。 實現方式
很多同學看到半平面交就覺得代碼一定十分高(e)端(xin)。但是這道題的半平面交十分特殊。
讓我們先看看這道例題。 例題
例題連結:【計算幾何】[HNOI2008][HYSBZ/BZOJ1007]水平可見直線
例題和這道題中的半平面都是在直線的上方。
例題是求組成半平面交的線段所在的直線是哪些。 回到這道題 具體做法
這道題也可以用同樣的方式求出半平面交。
組成半平面交和地面的都是線段,可看做分段函數。
令半平面交的函數為 f(x) f(x),地面為 g(x) g(x).
h(x)=f(x)−g(x) h(x)=f(x)-g(x)
ans=min(h(x)) ans=min(h(x))
考慮這f(x),g(x)兩個分段函數,每一段都是一次函數,h(x)顯然也是一次函數,分段的分界點顯然就是f(x),g(x)兩個函數的分界點。
而一次函數的極值顯然在 端點(分界點)處取得。
bingo
我們只需要枚舉f(x),g(x)的分界點,然後計算在分界點處的h(x)的值即可,只需要 O(n) O(n)的時間複雜度。
總時間複雜度 O(nlogn) O(nlogn)(排序的時間複雜度) 代碼
#include<cstdio>#include<algorithm>#include<cmath>#define MAXN 300using namespace std;#define EPS 1e-9#define INF 1e27struct 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);}