题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3685
ac代码:
#include<bits/stdc++.h> #define ll long long #define maxn 50010 using namespace std; const double zero=1e-8; struct node { double x,y; }p[maxn],q[maxn];//p保存原始点,q保存凸包的点 bool operator <(node a,node b) { if(fabs(a.y-b.y)>zero)//误差 return (a.y<b.y); else if(fabs(a.x-b.x)>zero) return (a.x<b.x); return false; } double det(node a,node b,node c)//叉积 { return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); } double dot(node a,node b,node c)//点积 { return (b.x-a.x)*(c.x-a.x)+(b.y-a.y)*(c.y-a.y); } double det3(node a,node b,node c)//叉积的三出口函数 { double d=det(a,b,c); if(fabs(d)<=zero)return 0; if(d<0)return-1; return 1; } void c_h_add(node li[],int &m0,int base,node p) { while(m0>base&&det3(li[m0-2],li[m0-1],p)<=0)--m0; li[m0]=p; m0++; } node c[maxn]; int n,m; void c_h(int &m0) { int i; for(i=0;i<n;i++) q[i]=p[i]; sort(q,q+n); m0=0; for(i=0;i<n;i++)c_h_add(c,m0,1,q[i]); int m1=m0; for(i=n-1;i>=0;i--)c_h_add(c,m0,m1,q[i]); for(i=0;i<m0;i++)q[i]=c[i]; m0--; } bool is_big_angle(node a,node b,node c)//判断以b为顶点的角是否是直角或钝角 { double Lcos=dot(b,a,c); if(Lcos>zero)return 0; return 1; } bool check(node zx,node a,node b)//判断zx到直线ab的垂足是否在线段ab上 { return (!is_big_angle(zx,a,b)&&!is_big_angle(zx,b,a)); } int main() { int t,i,j; scanf("%d",&t); while(t--) { scanf("%d",&n); for(i=0;i<n;i++) { scanf("%lf%lf",&p[i].x,&p[i].y); } node o,o1; o.x=o.y=0; double s=0,s1; for(i=1;i<n-1;i++) { o1.x=(p[0].x+p[i].x+p[i+1].x); o1.y=(p[0].y+p[i].y+p[i+1].y); s1=det(p[0],p[i],p[i+1]); s+=s1; o.x+=o1.x*s1; o.y+=o1.y*s1; } o.x=o.x/s/3.0;//除3放到最后除 o.y=o.y/s/3.0;//多边形重心 m=0; c_h(m); int ans=0; for(i=0;i<m-1;i++) { if(check(o,q[i],q[i+1]))ans++; } if(check(o,q[m-1],q[0]))ans++; cout<<ans<<endl; } return 0; }
求多边形重心模板:
https://www.cnblogs.com/hate13/p/4152302.html
#include<bits/stdc++.h> #define ll long long #define maxn 50010 using namespace std; const double zero=1e-8; struct node { double x,y; }p[maxn],q[maxn]; double det(node a,node b,node c)//叉积 { return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); } int main() { int t,i,j,n; scanf("%d",&t); while(t--) { scanf("%d",&n); for(i=0;i<n;i++) { scanf("%lf%lf",&p[i].x,&p[i].y); } node o,o1; o.x=o.y=0; double s=0,s1; for(i=1;i<n-1;i++) { o1.x=(p[0].x+p[i].x+p[i+1].x); o1.y=(p[0].y+p[i].y+p[i+1].y); s1=det(p[0],p[i],p[i+1]); s+=s1; o.x+=o1.x*s1; o.y+=o1.y*s1; } o.x=o.x/s/3.0; o.y=o.y/s/3.0;//多边形重心 printf("%.3f %.3f ",o.x,o.y); } return 0; }
求凸包模板:
https://www.cnblogs.com/wpbing/p/9456240.html
#include<bits/stdc++.h> using namespace std; struct point{ double x,y; point friend operator -(point a,point b) {return {a.x-b.x,a.y-b.y};} }p[105],s[105]; double dis(point a,point b) { point c=a-b; return sqrt(c.x*c.x+c.y*c.y); } double X(point a,point b) { return a.x*b.y-a.y*b.x; } int cmp(point a,point b) { double x=X(a-p[1],b-p[1]); if(x>0) return 1; if(x==0&&dis(a,p[1])<dis(b,p[1])) return 1; return 0; } double multi(point p1,point p2,point p3) { return X(p2-p1,p3-p1); } int main() { int N; while(scanf("%d",&N),N) { for(int i=1;i<=N;i++) cin>>p[i].x>>p[i].y; int k=1; for(int i=2;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[1],p[k]); sort(p+2,p+1+N,cmp); s[1]=p[1]; s[2]=p[2]; int t=2; for(int i=3;i<=N;i++) { while(t>=2&&multi(s[t-1],s[t],p[i])<=0) t--; s[++t]=p[i]; } double sum=0; for(int i=1;i<t;i++) { sum+=dis(s[i],s[i+1]);//s[i]凸包上的每个点 sum周长 } printf("%.2lf ",sum+dis(s[1],s[t])); } return 0; }
求凸包(有序 按逆时针或顺时针排序)
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int maxn=1005; const double PI=acos(-1.0); struct Point{ int x,y; Point():x(0),y(0){} Point(int x,int y):x(x),y(y){} }list[maxn]; int stack[maxn],top; //计算叉积p0p1×p0p2 int cross(Point p0,Point p1,Point p2){ return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y); } //计算p1p2的距离 double dis(Point p1,Point p2){ return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y)); } //极角排序函数,角度相同则距离小的在前面 bool cmp(Point p1,Point p2){ int tmp=cross(list[0],p1,p2); if(tmp>0) return true; else if(tmp==0&&dis(list[0],p1)<dis(list[0],p2)) return true; else return false; } //输入,把最左下角放在list[0],并且进行极角排序 void init(int n){ Point p0; scanf("%d%d",&list[0].x,&list[0].y); p0.x=list[0].x; p0.y=list[0].y; int k=0; for(int i=1;i<n;++i){ scanf("%d%d",&list[i].x,&list[i].y); if((p0.y>list[i].y)||((p0.y==list[i].y)&&(p0.x>list[i].x))){ p0.x=list[i].x; p0.y=list[i].y; k=i; } } list[k]=list[0]; list[0]=p0; sort(list+1,list+n,cmp); } //graham扫描法求凸包,凸包顶点存在stack栈中 //从栈底到栈顶一次是逆时针方向排列的 //如果要求凸包的一条边有2个以上的点 //那么要将while中的<=改成< //但这不能将最后一条边上的多个点保留 //因为排序时将距离近的点排在前面 //那么最后一条边上的点仅有距离最远的会被保留,其余的会被出栈 //所以最后一条边需要特判 void graham(int n){ if(n==1){ top=0; stack[0]=0; return; } top=1; stack[0]=0; stack[1]=1; for(int i=2;i<n;++i){ while(top>0&&cross(list[stack[top-1]],list[stack[top]],list[i])<=0) --top; stack[++top]=i; } } int main(){ int n,t; scanf("%d",&t); while(t--){ int id; scanf("%d %d",&id,&n); init(n); graham(n); printf("%d %d ",id,1+top); int kk=0; for(int i=1;i<=top;++i) if(list[stack[i]].y>list[stack[kk]].y||list[stack[i]].y==list[stack[kk]].y&&list[stack[i]].x<list[stack[kk]].x)kk=i; for(int i=kk;i>=0;i--)printf("%d %d ",list[stack[i]].x,list[stack[i]].y); for(int i=top;i>kk;i--)printf("%d %d ",list[stack[i]].x,list[stack[i]].y);//顺时针顺序输出每个点 /* double res=0;//求凸包周长 for(int i=0;i<top;++i) res+=dis(list[stack[i]],list[stack[i+1]]); res+=dis(list[stack[0]],list[stack[top]]); printf("%d ",(int)(res+0.5)); */ } return 0; }