1. 程式人生 > >poj 1113 Wall (andrew,graham求凸包)

poj 1113 Wall (andrew,graham求凸包)

題目連結:poj 1113

參考部落格:https://www.cnblogs.com/kuangbin/archive/2012/04/13/2445633.html

https://www.cnblogs.com/acgoto/p/9547049.html

 

題意:給出n個點,讓你把這n個點圍起來,有個前提,圍牆到頂點的距離要等於L,問圍牆有多長。

題解搬kuangbin神犇的。

題解:這道題的答案是凸包周長加上一個圓周長,即包圍凸包的一個圓角多邊形,但是沒弄明白那些圓角加起來為什麼恰好是一個圓。每個圓角是以凸包對應的頂點為圓心,給定的L為半徑,與相鄰兩條邊的切點之間的一段圓弧。每個圓弧的兩條半徑夾角與對應的凸包的內角互補。假設凸包有n條邊,則所有圓弧角之和為180°*n-180°*(n-2)=360°。故,圍牆周長為=n條平行於凸包的線段+n條圓弧的長度=凸包周長+圍牆離城堡距離L為半徑的圓周長。

 

程式碼:

andrew演算法和graham演算法求凸包,紫薯上說andrew更快,數值穩定性更好,因為andrew演算法只是按照座標排序,不同於graham演算法,是按極角排序,大量用叉積比較。

///andrew 求凸包 首尾相同

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;
const int maxn=1010;
const double PI=acos(-1.0);

struct point{
    int x,y;
    point(){}
    point(int _x,int _y){
        x=_x;y=_y;
    }
}p[maxn],ch[maxn];

point operator + (point a,point b) {return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b) {return point(a.x-b.x,a.y-b.y);}
point operator * (point a,int p) { return point(a.x*p,a.y*p);}
point operator / (point a,int p){ return point(a.x/p,a.y/p);}

bool operator < (const point &a,const point &b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double esp=1e-8;
int dcmp(double x){
    if(fabs(x)<esp) return 0;
    else return x<0?-1:1;
}
bool operator ==(const point &a,const point &b){
    return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}

int Cross(point a,point b) { return a.x*b.y-a.y*b.x;}
double Length(point a) { return sqrt(a.x*a.x*1.0+a.y*a.y*1.0);}

bool cmp(point a,point b) ///座標排序
{
    return (a.y<b.y||(a.y==b.y&&a.x<b.x));
}

int tot;
void andrew(int n)
{

    sort(p,p+n,cmp);

    tot=-1;
    for(int i=0;i<n;i++) ///構造凸包下側
    {
        while(tot>0&&Cross(ch[tot]-ch[tot-1],p[i]-ch[tot-1])<=0)
            tot--;
        ch[++tot]=p[i];
    }

    for(int i=n-2,k=tot;i>=0;i--){ ///構造凸包上側
        while(tot>k&&Cross(ch[tot]-ch[tot-1],p[i]-ch[tot-1])<=0)
            tot--;
        ch[++tot]=p[i];
    }
}

int main()
{
        int N,L;
        while(~scanf("%d%d",&N,&L))
        {

            for(int i=0;i<N;i++)
            {
                scanf("%d%d",&p[i].x,&p[i].y);
            }

            andrew(N);


            double sum=0;

            for(int i=0;i<tot;i++){
                sum+=Length(ch[i+1]-ch[i]);
            }
            sum+=Length(ch[tot]-ch[0]);

            sum+=2*PI*L;

            printf("%d\n",(int)(sum+0.5));

        }
        return 0;
}

 

///graham求凸包 首尾不同

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;
const int maxn=1010;
const double PI=acos(-1.0);

struct point{
    int x,y;
    point(){}
    point(int _x,int _y){
        x=_x;y=_y;
    }
}node[maxn];

point operator + (point a,point b) {return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b) {return point(a.x-b.x,a.y-b.y);}
point operator * (point a,int p) { return point(a.x*p,a.y*p);}
point operator / (point a,int p){ return point(a.x/p,a.y/p);}

bool operator < (const point &a,const point &b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double esp=1e-8;
int dcmp(double x){
    if(fabs(x)<esp) return 0;
    else return x<0?-1:1;
}
bool operator ==(const point &a,const point &b){
    return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}

int Cross(point a,point b) { return a.x*b.y-a.y*b.x;}
double Length(point a) { return sqrt(a.x*a.x+a.y*a.y);}

point p0; ///以p0為根據
bool cmp(point a,point b) ///極角排序函式,角度相同則距離小的在前面
{
    int tmp=Cross(a-p0,b-p0);
    if(tmp>0) return true;
    else if(tmp==0&&dcmp(Length(a-p0)-Length(b-p0))<0)
        return true;
    else return false;
}

int num[maxn],tot;

void graham(int n)
{
    tot=0;

    if(n==1){
        tot=0;num[0]=0;
    }

    if(n==2){
        tot=1;num[0]=0;num[1]=1;
    }

    if(n>2){
        tot=1;num[0]=0;num[1]=1;

        for(int i=2;i<n;i++)
        {
            while(tot>0&&Cross(node[num[tot]]-node[num[tot-1]],node[i]-node[num[tot-1]])<=0)
                tot--;
            num[++tot]=i;
        }
    }
}


int main()
{
        int N,L;
        while(~scanf("%d%d",&N,&L))
        {

            scanf("%d%d",&node[0].x,&node[0].y);

            int k=0;
            p0=point(node[0].x,node[0].y);

            for(int i=1;i<N;i++)
            {
                scanf("%d%d",&node[i].x,&node[i].y);

                if(p0.y>node[i].y||((p0.y==node[i].y)&&(p0.x>node[i].x))){
                    p0=node[i];
                    k=i;
                }
            }

            node[k]=node[0];
            node[0]=p0;
            sort(node+1,node+N,cmp);

            graham(N);


            double sum=0;
            for(int i=0;i<tot;i++){
                sum+=Length(node[num[i]]-node[num[i+1]]);
            }
            sum+=Length(node[num[tot]]-node[num[0]]);

            sum+=2*PI*L;

            printf("%d\n",(int)(sum+0.5));

        }
        return 0;
}