【凸包 Graham法 點集排序】poj 1113 Wall

CN_swords發表於2017-08-05

Link:http://poj.org/problem?id=1113

Graham法求凸包(O(Nlog2N))

將點排序,先按從下到上,y座標相等再按從左到右,排序後,正著遍歷一遍找到最低點到最高點滿足(即右邊部分的凸包),
反著遍歷一遍找到最高點到最低點滿足(即左邊部分的凸包)
注意:算個數的時候,演算法對於最低點和最高點都算了兩次,同時要考慮是否要共線。

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
/*
poj 1113
題意:要求建一堵圍牆圍繞城堡,牆於城堡的距離不小於L,輸入城堡每個節點的座標,求圍牆的最小長度。
題解:凸包加上360°的弧長。
*/
const int N = 1010;
const double PI = acos(-1.0);
struct Point
{
    double x,y;
};
bool cmp(Point aa,Point bb)
{
    if(aa.y != bb.y)    return aa.y < bb.y;
    return aa.x < bb.x;
}
 //是否嚴格左轉,共線不算(叉乘)
double xmult(Point a,Point b,Point c)   //(ca)×(cb)
{
    return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);
}
Point a[N];
int n,cnt,tail;
int tmp[N],ans[N];
void Jarvis()
{
    sort(a,a+n,cmp);
    cnt = tail = 0;
    tmp[tail++] = 0;
    tmp[tail++] = 1;
    for(int i = 2; i < n; i++)
    {
        while(tail>1 && xmult(a[tmp[tail-1]],a[i],a[tmp[tail-2]])<=0)
            tail--;
        tmp[tail++] = i;
    }
    for(int i = 0; i < tail; i++)  ans[cnt++] = tmp[i];
    tail = 0;
    tmp[tail++] = n-1;
    tmp[tail++] = n-2;
    for(int i = n-3; i >= 0; i--)
    {
        while(tail>1 && xmult(a[tmp[tail-1]],a[i],a[tmp[tail-2]])<=0)
            tail--;
        tmp[tail++] = i;
    }
    for(int i = 0; i < tail; i++)   ans[cnt++] = tmp[i];
}
double dist(Point p1,Point p2)
{
    return sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}
int main()
{
    double L;
    while(~scanf("%d%lf",&n,&L))
    {
        for(int i = 0; i < n; i++)
            scanf("%lf%lf",&a[i].x,&a[i].y);
        Jarvis();
        double res = 2*PI*L;
        for(int i = 0; i < cnt-1; i++)
            res += dist(a[ans[i]],a[ans[i+1]]);
        printf("%.0f\n",res);
    }
    return 0;
}


相關文章