【半平面交】【計算幾何】[BZOJ1038][ZJOI2008]瞭望塔

題目描述

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

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

僅包含一個實數,爲塔的最小高度,精確到小數點後三位。

樣例輸入

【輸入樣例一】
6
1 2 4 5 6 7
1 2 2 4 2 1
【輸入樣例二】
4
10 20 49 59
0 10 10 0

樣例輸出

【輸出樣例一】
1.000
【輸出樣例二】
14.500

HINT

對於100%的數據, N ≤ 300,輸入座標絕對值不超過106,注意考慮實數誤差帶來的問題。

題目分析

首先我們可以發現如果我們把每兩個點之間連線,作爲一個形如ax+by+c0 或者大於等於等關係的式子我們求這樣的式子的交集叫做求半平面交,那麼我們可以發現在本題中,答案一定出現在半平面交的集合中,對於每兩個點無論是村莊的輪廓點還是交集的邊界點,我們可以發現如果做垂直於x 軸的直線我們可以劃分成(令半平面交集的轉折點數量爲m 下面的村莊轉折點數量爲m )那麼我們可以將整個平面劃分成n+m+1 塊,在每一塊中間都是由上下兩個直線構成的,我們要求的答案就是每一塊中兩個直線距離最小的最小值,那麼在每一塊中因爲上下都是支線,那麼一定滿足單調性,我們檢查每一塊的左右端點取較小值就行了。
這裏提供一個求半平面交集的方法(僅限本題目, 可以想一下爲什麼):http://blog.csdn.net/jeremygjy/article/details/50623877
對於剛纔的說明,我畫了一張圖,求得就是每兩條綠色支線中黑色線段和紅色線段距離的最小值
這裏寫圖片描述

代碼

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <stack>
#include <cmath>
#define mcp(a,b) fabs((a)-(b))<eps
using namespace std;
const int MAXN = 300;
const double eps = 1e-8;
const int INF = 1000000000;
struct Point{
    double x, y;
    Point(){x=y=0;}
    bool operator<(const Point& pt) const{
        return x < pt.x;
    }
    bool operator>(Point pt) const{
        return x > pt.x;
    }
}List[MAXN+10], List2[MAXN+10];
struct Line{
    double a, b;
    Point GetD(const Line& c){
        Point ret;
        ret.x = (1.0 * (c.b - b)) / (a - c.a);
        ret.y = a * ret.x + b;
        return ret;
    }
    void Make(Point _a, Point _b){
        a = (_a.y - _b.y) / (_a.x - _b.x);
        b = _a.y - a * _a.x;
    }
    bool operator == (const Line& c) {
        return c.a + eps >= a && c.a - eps <= a;
    }
}T[MAXN+10];
stack<Line> ans;
stack<Point> jd;
bool cmp(Line a, Line b){
    if(mcp(a.a, b.a))
        return a.b < b.b;
    return a.a < b.a;
}
bool cmp3(Point a, Point b){
    return a.x < b.x;
}
int pcmp(Point p, Line l){
    double y = l.a * p.x + l.b;
    if(y >= p.y-eps)
        return -1;
    return 1;
}
void solve(int n){
    int tn = 0;
    sort(T+1, T+1+n, cmp);
    for(int i=1;i<=n;i++){
        while(mcp(T[i].a,T[i+1].a)) i++;
        T[++tn] = T[i];
    }
    ans.push(T[1]);
    for(int i=2;i<=tn;i++){
        while(!jd.empty()){
            Point tp = jd.top();
            if(pcmp(tp, T[i]) <= 0){
                jd.pop();
                ans.pop();
            }else break;
        }
        jd.push(T[i].GetD(ans.top()));
        ans.push(T[i]);
    }
}
int main(){
    Line tmp;
    int n;
    scanf("%d", &n);
    for(int i=1;i<=n;i++) scanf("%lf", &List[i].x);
    for(int i=1;i<=n;i++) scanf("%lf", &List[i].y);
    for(int i=2;i<=n;i++)
        T[i-1].Make(List[i-1], List[i]);
    solve(n-1);
    int cnt = 0;
    while(!jd.empty()){
        List2[++cnt] = jd.top();
        jd.pop();
    }
    sort(List2+1, List2+1+cnt, cmp3);
    double answer = 1e20;
    for(int i=1;i<n;i++){
        tmp.Make(List[i], List[i+1]);
        int pos = lower_bound(List2+1, List2+1+cnt, List[i]) - List2;
        for(int j=pos;List2[j].x <= List[i+1].x+eps && j <= cnt;j++)
            answer = min(answer, max(0.0 , List2[j].y - List2[j].x*tmp.a - tmp.b));
    }
    int ct2 = ans.size();
    int ctmp = ct2;
    while(!ans.empty()){
        T[--ct2] = ans.top();
        ans.pop();
    }
    ct2 = ctmp;
    int tp = lower_bound(List+1, List+1+n, List2[1]) - List;
    if(List[tp].x >= List2[1].x-eps) tp--;
    while(tp){
        answer = min(answer, max(0.0, T[0].a*List[tp].x+T[0].b-List[tp].y));
        tp--;
    }
    tp = lower_bound(List+1, List+1+n, List2[cnt]) - List;
    while(tp <= n){
        answer = min(answer, max(0.0, T[cnt].a*List[tp].x+T[cnt].b-List[tp].y));
        tp++;
    }
    for(int i=1;i<cnt;i++){
        tp = lower_bound(List+1, List+1+n, List2[i]) - List;
        for(int j=tp;List[j].x <= List2[i+1].x && j <= n;j++)
            answer = min(answer, max(0.0, T[i].a*List[j].x+T[i].b-List[j].y));
    }
    printf("%.3lf\n", answer);

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