注:《黑书》P392中有讲:对于通常求的凸包,输出的凸包是包括所有可能的共线点还是不包括任何共线点(即只有极点)这是取决于题目的题目要求的。在下面的程序中都会将去共线点的代码加上,视题目删除。
1、http://poj.org/problem?id=1113 简单凸包,求凸包的周长和圆周
Graham-Scan 算法
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<cstdlib> #include<queue> #include<stack> #include<map> #include<vector> #include<algorithm> #include<ctime> using namespace std; #define maxn 1005 #define eps 1e-6 #define pi acos(-1.0) struct point { double x,y; }p[maxn],sta[maxn]; int top; int Fabs(double d) { if(fabs(d)<eps) return 0; else return d>0?1:-1; } double x_multi(point p1,point p2,point p3) { return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y); } double Dis(point p1,point p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } bool cmp(point a,point b)//按极角排序 { if(Fabs(x_multi(p[0],a,b))>0) return 1; if(Fabs(x_multi(p[0],a,b))<0) return 0; if(Fabs(Dis(p[0],a)-Dis(p[0],b))<0) return 1; return 0; } void Graham(int n) { int i,k=0,tot; for(i=1;i<n;i++) if((p[i].y<p[k].y)||((p[i].y==p[k].y)&&(p[i].x<p[k].x))) k=i; swap(p[0],p[k]); sort(p+1,p+n,cmp); tot=1; for(i=2;i<n;i++) //去共线点 if (Fabs(x_multi(p[i],p[i-1],p[0]))) p[tot++]=p[i-1]; p[tot++]=p[n-1]; sta[0]=p[0],sta[1]=p[1]; i=top=1; for(i=2;i<tot;i++) { while(top>=1&&Fabs(x_multi(p[i],sta[top],sta[top-1]))>=0) { if(top==0) break; top--; } sta[++top]=p[i]; } } int main() { int n,i; double r,ans=0.0; scanf("%d%lf",&n,&r); for(i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); Graham(n); for(i=0;i<=top;i++) ans+=Dis(sta[i],sta[(i+1)%(top+1)]); printf("%.0lf\n",ans+2.0*pi*r); return 0; }
Graham求凸包+叉积求有向面积
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<cstdlib> #include<queue> #include<stack> #include<map> #include<vector> #include<algorithm> #include<ctime> using namespace std; #define maxn 1005 #define eps 1e-6 #define pi acos(-1.0) struct point { double x,y; }p[maxn],sta[maxn]; int top; int Fabs(double d) { if(fabs(d)<eps) return 0; else return d>0?1:-1; } double x_multi(point p1,point p2,point p3) { return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y); } double Dis(point p1,point p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } bool cmp(point a,point b) { if(Fabs(x_multi(p[0],a,b))>0) return 1; if(Fabs(x_multi(p[0],a,b))<0) return 0; if(Fabs(Dis(p[0],a)-Dis(p[0],b))<0) return 1; return 0; } void Graham(int n) { int i,k=0,tot; for(i=1;i<n;i++) if((p[i].y<p[k].y)||((p[i].y==p[k].y)&&(p[i].x<p[k].x))) k=i; swap(p[0],p[k]); sort(p+1,p+n,cmp); tot=1; for(i=2;i<n;i++) if (Fabs(x_multi(p[i],p[i-1],p[0]))) p[tot++]=p[i-1]; p[tot++]=p[n-1]; sta[0]=p[0],sta[1]=p[1]; i=top=1; for(i=2;i<tot;i++) { while(top>=1&&Fabs(x_multi(p[i],sta[top],sta[top-1]))>=0) { if(top==0) break; top--; } sta[++top]=p[i]; } } int main() { int n,i; double ans=0.0; scanf("%d",&n); for(i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); Graham(n); for(i=1;i<top;i++) ans+=x_multi(sta[0],sta[i],sta[i+1])/2; printf("%.0lf\n",(floor)(ans/50)); return 0; }
题目讨论区中因此大家也给出一些结论,题目给出的点个数至少为6个,所有点不共线,凸包每条边上至少有三个点即一条边除去极点外还有其他的点。
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<cstdlib> #include<queue> #include<stack> #include<map> #include<vector> #include<algorithm> #include<ctime> using namespace std; #define maxn 1005 #define eps 1e-6 #define pi acos(-1.0) int Fabs(double d) { if(fabs(d)<eps) return 0; else return d>0?1:-1; } struct point { double x,y; bool operator == (const point &p) { return Fabs(x-p.x)==0&&Fabs(y-p.y)==0; } }p[maxn],pp[maxn],sta[maxn]; int top; double x_multi(point p1,point p2,point p3) { return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y); } double Dis(point p1,point p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } bool cmp(point a,point b) { if(Fabs(x_multi(p[0],a,b))>0) return 1; if(Fabs(x_multi(p[0],a,b))<0) return 0; if(Fabs(Dis(p[0],a)-Dis(p[0],b))<0) return 1; return 0; } bool Onsegment(point p1,point p2,point p3) { double min_x=min(p1.x,p2.x); double min_y=min(p1.y,p2.y); double max_x=max(p1.x,p2.x); double max_y=max(p1.y,p2.y); if(p3.x>=min_x&&p3.x<=max_x&&p3.y>=min_y&&p3.y<=max_y) return true; return false; } void Graham(int n) { int i,k=0,tot; for(i=1;i<n;i++) if((p[i].y<p[k].y)||((p[i].y==p[k].y)&&(p[i].x<p[k].x))) k=i; swap(p[0],p[k]); sort(p+1,p+n,cmp); tot=1; for(i=2;i<n;i++) if (Fabs(x_multi(p[i],p[i-1],p[0]))) p[tot++]=p[i-1]; p[tot++]=p[n-1]; sta[0]=p[0],sta[1]=p[1]; i=top=1; for(i=2;i<tot;i++) { while(top>=1&&Fabs(x_multi(p[i],sta[top],sta[top-1]))>=0) { if(top==0) break; top--; } sta[++top]=p[i]; } } int main() { int t,n,i,j; bool flag; scanf("%d",&t); while(t--) { scanf("%d%",&n); for(i=0;i<n;i++) { scanf("%lf%lf",&p[i].x,&p[i].y); pp[i]=p[i]; } if(n<6) { puts("NO"); continue; } flag=false; for(i=2;i<n;i++)//判断所有的点是否共线 if(Fabs(x_multi(p[0],p[1],p[i]))!=0) { flag=true; break; } if(!flag) { puts("NO"); continue; } Graham(n); for(i=0;i<=top;i++) { flag=false; for(j=0;j<n;j++)//判断每条边上除了极点还有其他的点 if(!(pp[j]==sta[i])&&!(pp[j]==sta[(i+1)%(top+1)])) if(Onsegment(sta[i],sta[(i+1)%(top+1)],pp[j])) { flag=true; break; } if(!flag) break; } printf("%s\n",flag?"YES":"NO"); } return 0; }
《黑书》中有以求平面点集最远点对距离,可以利用凸包性质改进最坏情况时间复杂度。本题数据较水,可以Graham后直接暴力,黑书推荐方法就是旋转卡壳。
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<cstdlib> #include<queue> #include<stack> #include<map> #include<vector> #include<algorithm> #include<ctime> using namespace std; #define maxn 50005 #define eps 1e-6 #define pi acos(-1.0) int Fabs(double d) { if(fabs(d)<eps) return 0; else return d>0?1:-1; } struct point { double x,y; bool operator == (const point &p) { return Fabs(x-p.x)==0&&Fabs(y-p.y)==0; } }p[maxn],sta[maxn]; int top; double x_multi(point p1,point p2,point p3) { return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y); } double Dis(point p1,point p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } bool cmp(point a,point b) { if(Fabs(x_multi(p[0],a,b))>0) return 1; if(Fabs(x_multi(p[0],a,b))<0) return 0; if(Fabs(Dis(p[0],a)-Dis(p[0],b))<0) return 1; return 0; } void Graham(int n) { int i,k=0,tot; for(i=1;i<n;i++) if((p[i].y<p[k].y)||((p[i].y==p[k].y)&&(p[i].x<p[k].x))) k=i; swap(p[0],p[k]); sort(p+1,p+n,cmp); tot=1; for(i=2;i<n;i++) if (Fabs(x_multi(p[i],p[i-1],p[0]))) p[tot++]=p[i-1]; p[tot++]=p[n-1]; sta[0]=p[0],sta[1]=p[1]; i=top=1; for(i=2;i<tot;i++) { while(top>=1&&Fabs(x_multi(p[i],sta[top],sta[top-1]))>=0) { if(top==0) break; top--; } sta[++top]=p[i]; } } int main() { int n,i,j; while(~scanf("%d",&n)) { for(i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); double ans=0.0,tmp; Graham(n); for(i=0;i<=top;i++) for(j=i+1;j<=top;j++) { tmp=Dis(sta[i],sta[j]); if(tmp>ans) ans=tmp; } /*旋转卡壳*/ /*sta[top+1]=sta[0]; for(i=0,j=1;i<=top;i++) { while(x_multi(sta[i],sta[i+1],sta[j+1])>x_multi(sta[i],sta[i+1],sta[j])) j=(j+1)%(top+1); ans=max(ans,max(Dis(sta[i+1],sta[j+1]),Dis(sta[i],sta[j]))); }*/ printf("%.0lf\n",ans*ans); } return 0; }