几何模版-凸包
Graham算法
平均复杂度:N log(N)
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int Max = 1100; #define PI 3.1415926 struct Point { int x; int y; }; int num; Point p[Max]; //原始点 Point ch[Max]; //凸包点 //叉乘 int xmult(const Point& p0, const Point& p1, const Point& p2) { return (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y); } //间距 double dis(const Point& a, const Point& b) { return sqrt((double)(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } //极角排序 bool cmp(const Point& a, const Point& b) { int ret = xmult(p[0],a,b); if(ret>0) return true; if(ret==0&&dis(p[0],a)<dis(p[0],b)) return true; return false; } //Graham(p,n,ch,num); //求n个点构成的p点集中的凸包 //凸包上共num个点按序存于ch数组里。 void Graham(Point* p, int n, Point* ch, int& top) { int i,k=0; for(i=1; i < n; i++) { if(p[k].y>p[i].y || ((p[k].y==p[i].y)&&p[k].x>p[i].x)) k = i; } swap(p[k],p[0]);//寻找最左下角的点 sort(p+1,p+n,cmp); top = 0; ch[top++] = p[0]; ch[top++] = p[1]; ch[top++] = p[2]; for(i = 3; i < n; ch[top++]=p[i++]) while(top>=2 && xmult(ch[top-2],ch[top-1],p[i])<0) //左拐判断 top--; } //test: int main() { int i; int n,r,t; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&r); { for(i = 0; i < n; i++) scanf("%d%d",&p[i].x,&p[i].y); Graham(p,n,ch,num); double result = PI*r*2; for(i = 0; i < num-1; i++)//周长 result += dis(ch[i],ch[i+1]); result += dis(ch[0],ch[num-1]); printf("%.0f\n",result); } if(t!=0) printf("\n"); } return 0; }
快速凸包算法
复杂度:平均Nlog(2N),最优N,最差N^2
#include <stdio.h> #include <string.h> #include <iostream> #include <math.h> #include <algorithm> using namespace std; struct point { double x,y; }; struct point p[105],p1[105]; double s[105]; int d; bool cmp(point a,point b) { if(a.x==b.x) return a.y<b.y; return a.x<b.x; } double chaji(point a,point b,point c) { return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y); } //求点集p的凸包,凸包上点的个数为d。按序存入p1数组内 void hull(int l,int r,point a,point b) { //左边 右边 int x=l,i=l-1,j=r+1,k; point y; for(k=l;k<=r;k++) if(s[x]<s[k] || s[x]==s[k] && cmp(p[x],p[k])) x=k; y=p[x]; for(k=l;k<=r;k++) //左边 { s[++i]=chaji(p[k],a,y); if(s[i]>0) swap(p[i],p[k]); else i--; } for(k=r;k>=l;k--) // 右边 { s[--j]=chaji(p[k],y,b); if(s[j]>0) swap(p[j],p[k]); else j++; } if(l<=i) hull(l,i,a,y); d++; p1[d]=y; if(j<=r) hull(j,r,y,b); } double dist(point a,point b) { return sqrt((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y)); } int main() { int n,i; double sum; while(~scanf("%d",&n) && n) { memset(p,0,sizeof(p)); memset(p1,0,sizeof(p1)); memset(s,0,sizeof(s)); sum=0; d=1; for (i=1;i<=n;i++) scanf("%lf %lf",&p[i].x,&p[i].y); sort(p+1,p+n+1,cmp); //按x从小到大排,x相同:按y从小到大排 p1[1].x=p[1].x; //第一个点 一定为凸包点 p1[1].y=p[1].y; hull(2,n,p[1],p[1]); p1[d+1].x=p[1].x; p1[d+1].y=p[1].y; for(i=1;i<=d;i++) sum+=dist(p1[i],p1[i+1]); if(d==2) sum/=2; printf("%.2lf\n",sum); } return 0; }
步进发
复杂度:平均 NM(M为凸包上的点的个数)
适合凸包上的点个数少的时候。即点分布较为随机。
#include <stdio.h> #include <math.h> #include <algorithm> using namespace std; struct point { double x,y; }; struct point a[1005],b[1005],p[2005],p1[2005]; //p:遍历全部点 p1:记录凸包的点 int size; //凸包点个数 bool cmp(point a,point b) {//按x从小到大排列;x相同的 按y从小到大排列 if(a.x==b.x) return a.y<b.y; return a.x<b.x; } double chaji(point a,point b,point c) { return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); } double dist(point a,point b) { return sqrt((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y)); } //求点集p的凸包。 //凸包上点数为size,按序存回p数组; void sovle(int n) { int i; double t; p1[0]=p[0]; //第一个凸包点是第一个点 p1[1]=p[1]; //假设..二............二.... size=1; //记录当前假设的凸包点 for(i=2;i<n;i++)//遍历所有点 { t=chaji(p1[size-1],p1[size],p[i]);//算两个凸包点与该点 向量的叉积 while(t<=0)//叉积<=0 该点不是凸包点 { size--; //垂直 或者凸包点全部遍历完 if(t==0 || size==0) break; t=chaji(p1[size-1],p1[size],p[i]);//直到该点遍历完全部凸包点或者 满足>0为凸包点 } p1[++size]=p[i];//记录下一个凸包点 } p1[++size]=p[n-2]; for(i=n-3;i>=0;i--)//往回找 直到第一个凸包点 { t=chaji(p1[size-1],p1[size],p[i]); while(t<=0) { size--; if(t==0 || size==0) break; t=chaji(p1[size-1],p1[size],p[i]); } p1[++size]=p[i]; } } int main() { int n,i; double sum; while(~scanf("%d",&n) && n) { for(i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); sort(p,p+n,cmp); sovle(n); sum=0; //计算周长 for(i=0;i<size;i++) sum=sum+dist(p1[i],p1[i+1]); //若只有2个凸包点 if(size==2) sum=sum/2; printf("%.2lf\n",sum); } return 0; }