2017 ACM-ICPC world final A.Airport Construction 計算幾何

鏈接:

Airport Construction

題意:

逆時針給一個簡單多邊形(不一定爲凸多邊形)的每一條邊,問內部可放置的最長線段的長度。數據保證無兩條邊共線。

思路:

首先,由於不一定是凸多邊形,直接用凸包旋轉卡殼求直徑方法是不對的。

考慮最長的線段何時取到:假設線段不過兩個頂點,其長度總可以通過繼續旋轉線段使其碰到頂點而繼續增大。由反證法可證線段一定過兩個頂點。那麼線段端點一定爲頂點嗎?不一定。

上圖即是一個反例。

考慮到點的個數<=200,這個規模O(n^4)複雜度可能都不會TLE,自然想到枚舉多邊形的每兩點,連成直線,求直線在多邊形內連續的每條線段,更新答案。

問題轉化爲怎樣求直線被多邊形切後的連續線段長度。可以採用多邊形與直線的交點作爲切分點,求每個切分點之間的線段。

由於邊以逆時針順序給出,故可判斷每條邊的兩端點和直線的位置關係。若兩點在直線異側或者一點在直線上,則此邊將直線切斷,交點作爲切分點;若兩點在直線同側,無任何影響;若兩點均在直線上,相當於在直線上多了兩個切分點。在得到一系列切分點之後,根據座標排個序,遍歷判斷相鄰切分點之間的每條線段在多邊形內還是多邊形外,如果在多邊形內,局部答案加上這條線段長度,如果在多邊形外,更新全局答案,局部答案清零。

附:官方題解

代碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-8;
static const double pi = acos(-1);

void redirect(){
    #ifdef LOCAL
        freopen("test.txt","r",stdin);
    #endif
}
int n;
double ans = 0;
inline int sgn(double x){
    return fabs(x)<eps?0:(x>0)?1:-1;
}
struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x = _x,y = _y;
    }
    bool operator ==(const Point &b)const{
        return (sgn(x-b.x)==0)&&(sgn(y-b.y)==0)?true:false;
    }
    bool operator <(const Point &b)const{
        return (sgn(x-b.x)==0)?(y < b.y):(x < b.x);
    }
    Point operator -(const Point &b)const{
        return Point(x-b.x,y-b.y);
    }
    double operator ^(const Point &b)const{
		return x*b.y - y*b.x;
	}
    double operator *(const Point &b)const{
        return x*b.x + y*b.y;
    }
}p[210];
struct Line{
    Point s,t;
    Line(){}
    Line(Point _s,Point _t){
        s = _s,t = _t;
    }
    Point crosspoint(Line v){
    	double a1 = (v.t-v.s)^(s-v.s);
    	double a2 = (v.t-v.s)^(t-v.s);
    	return Point((s.x*a2-t.x*a1)/(a2-a1),(s.y*a2-t.y*a1)/(a2-a1));
    }
    bool pointonseg(Point q){
	   return sgn((q-s)^(t-s)) == 0 && sgn((q-s)*(q-t)) <= 0;
    }
};
inline bool pointonpoly(Point q){
    int res = 0;
    for(int i = 0;i < n;i++){
        if(Line(p[i],p[i+1]).pointonseg(q))return true;
        int d1 = sgn(p[i].y - q.y), 
            d2 = sgn(p[i+1].y - q.y),
            k = sgn((p[i+1] - p[i])^(q - p[i]));
        if (k > 0 && d1 <= 0 && d2 > 0)res++;
        if (k < 0 && d2 <= 0 && d1 > 0)res--;
    }
    return res ? true : false;
}
inline double distance(const Point& a,const Point& b){
    return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}

inline void attempt(const int& p1,const int& p2){
    vector<Point>v;
    Line l(p[p1],p[p2]);
    for(int i = 0;i < n;i++){
        if(sgn((l.t-l.s)^(p[i]-l.s)) * sgn((l.t-l.s)^(p[i+1]-l.s)) <= 0){
            Point v1 = l.t - l.s,v2 = p[i+1] - p[i];
            if(sgn(v1^v2) == 0){
                v.push_back(p[i]);
                v.push_back(p[i+1]);
            }
            else v.push_back(l.crosspoint(Line(p[i],p[i+1])));
        }
    }
    sort(v.begin(),v.end());
    v.resize(unique(v.begin(),v.end()) - v.begin());
    int cnt = v.size();
    double res = 0;
    for(int i = 1;i < cnt;i++){
        if(pointonpoly(Point((v[i-1].x+v[i].x)/2.0,(v[i-1].y+v[i].y)/2.0)))
        res += distance(v[i-1],v[i]);
        else{
            res = 0;
            if(distance(v[i],v.back()) <= ans)return;
        }
        ans = max(ans,res);
    }
}
int main(){
    redirect();
    scanf("%d",&n);
    for(int i = 0;i < n;i++)scanf("%lf %lf",&p[i].x,&p[i].y);
    p[n].x = p[0].x,p[n].y = p[0].y;
    for(int i = 0;i < n;i++)
    for(int j = i+1;j < n;j++)
    attempt(i,j);
    printf("%.9f\n",ans);
    return 0;
}

附,一組自己查錯樣例

10
0 0
3 4
6 0
9 4
12 0
12 10
9 6
6 10
3 6
0 10

答案爲12.649110641

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