poj 1113 Wall (andrew,graham求凸包)
阿新 • • 發佈:2018-11-16
題目連結: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; }