ZJOI 2008 瞭望塔 三分法

題目鏈接bzoj點我:-) 洛谷點我:-)

題目描述
致力於建設全國示範和諧小村莊的H村村長dadzhi,決定在村中建立一個瞭望塔,以此加強村中的治安。
我們將H村抽象爲一維的輪廓。如下圖所示
這裏寫圖片描述
我們可以用一條山的上方輪廓折線(x1, y1), (x2, y2), …. (xn, yn)來描述H村的形狀,這裏x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]間的任意位置, 但必須滿足從瞭望塔的頂端可以看到H村的任意位置。可見在不同的位置建造瞭望塔,所需要建造的高度是不同的。爲了節省開支,dadzhi村長希望建造的塔高度儘可能小。
請你寫一個程序,幫助dadzhi村長計算塔的最小高度。

輸入格式
輸入文件tower.in第一行包含一個整數n,表示輪廓折線的節點數目。接下來第一行n個整數, 爲x1 ~ xn. 第三行n個整數,爲y1 ~ yn。

輸出格式
輸出文件tower.out僅包含一個實數,爲塔的最小高度,精確到小數點後三位。

思路
首先,我們發現把每段輪廓線看作一條直線,那麼所有直線左邊的公共部分就是瞭望塔最終應該在的位置範圍,樣例如圖:
這裏寫圖片描述
想到這裏,半平面交可做了。

接下來,考慮兩個相鄰的端點x, x+1,可以發現它們之間的那一段答案是單峯的,所以用三分法解決即可。
單峯性的證明:
當我們討論瞭望塔的位置在 x 和 x+1 之間時 , 這一段區間上方的瞭望塔區間一定爲一個下凸的單峯,可以分類討論x至x+1的情況,可以發現不管是上升下降還是平的,答案都是一個單峯

(稍嚴謹的證明:當我們討論瞭望塔的位置在 i 和 i+1 之間時 , 其他的直線可以組成一個下凸的半平面 , 將整個圖形旋轉使得直線水平 , 可知下凸的半平面仍保持其性質。 那麼瞭望塔的高度在此線段上保持單峯性)

感想
三分真神奇。。
但是。還是要碼一碼半平面交的。。畢竟。。沒寫過。。

代碼

//miaomiao 2017.2.8
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;

#define For(i, a, b) for(int i = (a); i <= (int)(b); i++)
#define N (300+5)
#define eps 1e-9

int n;
double x[N], y[N], ret, len;

inline double calc(int i, double xi){
    ret = 0, len = y[i]+(y[i+1]-y[i])/(x[i+1]-x[i])*(xi-x[i]);

    For(j, 1, n){
        if(i==j || i+1==j) continue;
        int a = j+(j<i? 1: -1);
        double h = y[j]+(y[a]-y[j])/(x[a]-x[j])*(xi-x[j]);
        ret = max(ret, h-len);
    }
    return ret;
}

int main(){
    scanf("%d", &n);
    For(i, 1, n) scanf("%lf", &x[i]); For(i, 1, n) scanf("%lf", &y[i]);

    double ans = 1.0*(1e20);
    For(i, 1, n-1){
        double lm, rm, mid, L = x[i], R = x[i+1];
        while(fabs(R-L) > eps){
            mid = (R-L)/3.0; lm = L+mid, rm = R-mid;

            if(calc(i, lm) > calc(i, rm)) L = lm;
            else R = rm;
        }
        ans = min(ans, calc(i, L));
    }

    if(n == 1) ans = 0;
    printf("%.3lf\n", ans);

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章