ACM几何入门---(二维凸包)以及入门题总结

    xiaoxiao2022-07-06  204

    最近学了下二维的凸包,打算去摸一摸这个专题!!!

    这里的话,我觉得我会去把这个专题写完

    板子

    const int N=50005; struct Point { double x,y; }; Point stackk[N]; //凸包中所有点,下标从0....top开始 Point p[N]; //存入的所有点 Point MinA; int top; double dist(Point A,Point B) { return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)); } //计算叉积 判断bc向量到ac向量 是否通过左转得到 >0左转 <0右转 =0共线 double cross(Point A,Point B,Point C) { return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x); } bool cmp(Point a,Point b) //极角排序 { double k=cross(MinA,a,b); if(k>0) return 1; if(k<0) return 0; return dist(MinA,a)<dist(MinA,b); } void Graham(int n) { int i; for(i=1; i<n; i++) if(p[i].y<p[0].y||(p[i].y==p[0].y&&p[i].x<p[0].x)) swap(p[i],p[0]); MinA=p[0]; sort(p+1,p+n,cmp); stackk[0]=p[0]; stackk[1]=p[1]; top=1; for(i=2; i<n; i++) { //注意这里我们把共线的点也压入凸包里 //是否满足叉乘大于0 不满足出栈 控制<0或<=0可以控制重点,共线 while(cross(stackk[top-1],stackk[top],p[i])<=0&&top>=1) --top; stackk[++top]=p[i]; } top++; }

    第一题(凸包求周长)

    题目传送门HDU 1392 Surround the Trees **题意:**让你把树围起来,然后凸包搞一下,记得特判n = 1, n =2就行

    AC代码

    #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<math.h> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define PI 3.14159265358979323846 using namespace std; typedef long long int ll; typedef unsigned long long int ull; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } const int N=50005; struct Point { double x,y; }; Point stackk[N]; //凸包中所有点,下标从0....top开始 Point p[N]; //存入的所有点 Point MinA; int top; double dist(Point A,Point B) { return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)); } //计算叉积 判断bc向量到ac向量 是否通过左转得到 >0左转 <0右转 =0共线 double cross(Point A,Point B,Point C) { return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x); } bool cmp(Point a,Point b) //极角排序 { double k=cross(MinA,a,b); if(k>0) return 1; if(k<0) return 0; return dist(MinA,a)<dist(MinA,b); } void Graham(int n) { int i; for(i=1; i<n; i++) if(p[i].y<p[0].y||(p[i].y==p[0].y&&p[i].x<p[0].x)) swap(p[i],p[0]); MinA=p[0]; sort(p+1,p+n,cmp); stackk[0]=p[0]; stackk[1]=p[1]; top=1; for(i=2; i<n; i++) { //注意这里我们把共线的点也压入凸包里 //是否满足叉乘大于0 不满足出栈 控制<0或<=0可以控制重点,共线 while(cross(stackk[top-1],stackk[top],p[i])<=0&&top>=1) --top; stackk[++top]=p[i]; } top++; } int n; // 求凸包的周长,然后考虑这个n ==1,2特例 void solve(){ for(int i = 0; i < n; i++){ cin>>p[i].x>>p[i].y; } Graham(n); double ans = 0.0; Point tmp = stackk[0]; for(int i = 1; i < top ;i++){ ans += dist(stackk[i-1],stackk[i]); } ans += dist(stackk[top-1],stackk[0]); if(n == 1){ ans = 0.0; } if(n == 2){ ans = dist(p[0],p[1]); } printf("%.2lf\n",ans); } int main(){ while(cin>>n){ if(n == 0) break; solve(); } return 0; }

    第二题(凸包的周长 + 圆的周长)

    题目传送门HDU 1348 Wall 题意: 最后就是让你求凸包的周长+圆的周长 需要注意的就是换行

    AC代码

    #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<math.h> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define PI 3.14159265358979323846 using namespace std; typedef long long int ll; typedef unsigned long long int ull; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } const int N=50005; struct Point { double x,y; }; Point stackk[N]; //凸包中所有点,下标从0....top开始 Point p[N]; //存入的所有点 Point MinA; int top; double dist(Point A,Point B) { return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)); } //计算叉积 判断bc向量到ac向量 是否通过左转得到 >0左转 <0右转 =0共线 double cross(Point A,Point B,Point C) { return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x); } bool cmp(Point a,Point b) //极角排序 { double k=cross(MinA,a,b); if(k>0) return 1; if(k<0) return 0; return dist(MinA,a)<dist(MinA,b); } void Graham(int n) { int i; for(i=1; i<n; i++) if(p[i].y<p[0].y||(p[i].y==p[0].y&&p[i].x<p[0].x)) swap(p[i],p[0]); MinA=p[0]; sort(p+1,p+n,cmp); stackk[0]=p[0]; stackk[1]=p[1]; top=1; for(i=2; i<n; i++) { //注意这里我们把共线的点也压入凸包里 //是否满足叉乘大于0 不满足出栈 控制<0或<=0可以控制重点,共线 while(cross(stackk[top-1],stackk[top],p[i])<=0&&top>=1) --top; stackk[++top]=p[i]; } top++; } int n; double L; // 求凸包的周长+圆的周长 // 这个题目需要主要的就是最后一个数据不需要两个换行 void solve(){ cin>>n>>L; for(int i = 0; i < n; i++){ cin>>p[i].x>>p[i].y; } Graham(n); double ans = 0.0; Point tmp = stackk[0]; for(int i = 1; i < top ;i++){ ans += dist(stackk[i-1],stackk[i]); } ans += dist(stackk[top-1],stackk[0]); ans += 2 * PI * L; printf("%.0lf\n",ans); } int main(){ int t; cin>>t; while(t--){ solve(); if(t!=0){ printf("\n"); } } return 0; }

    第三题(凸包+旋转卡壳求最大三角形面积)

    题目传送门poj 2079 Triangle **题意:**给你n个点,然后让你去求一个最大的三角形的面积 一个面积最大的话,一定是凸包上的点,具体… 说不明白,我也是看博客记住的,然后的话,就是+旋转卡壳… 都是板子

    AC代码

    #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<math.h> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define PI 3.14159265358979323846 using namespace std; typedef long long int ll; typedef unsigned long long int ull; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } const int N=50005; struct Point { double x,y; }; Point stackk[N]; //凸包中所有点,下标从0....top开始 Point p[N]; //存入的所有点 Point MinA; int top; double dist(Point A,Point B) { return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)); } //计算叉积 判断bc向量到ac向量 是否通过左转得到 >0左转 <0右转 =0共线 double cross(Point A,Point B,Point C) { return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x); } bool cmp(Point a,Point b) //极角排序 { double k=cross(MinA,a,b); if(k>0) return 1; if(k<0) return 0; return dist(MinA,a)<dist(MinA,b); } void Graham(int n) { int i; for(i=1; i<n; i++) if(p[i].y<p[0].y||(p[i].y==p[0].y&&p[i].x<p[0].x)) swap(p[i],p[0]); MinA=p[0]; sort(p+1,p+n,cmp); stackk[0]=p[0]; stackk[1]=p[1]; top=1; for(i=2; i<n; i++) { //注意这里我们把共线的点也压入凸包里 //是否满足叉乘大于0 不满足出栈 控制<0或<=0可以控制重点,共线 while(cross(stackk[top-1],stackk[top],p[i])<=0&&top>=1) --top; stackk[++top]=p[i]; } top++; } int n; double L; // n个点组成的三角形面积最大 // 旋转卡壳 // 注意的就是输出的时候换成 .2f double rotating_calipers(int n) //n个点 { if(n <= 2){ return 0.00; } int j=1,k=0; double ans=0; for(int i=0;i<n;i++) { j=(i+1)%n; k=(j+1)%n; while(fabs(cross(stackk[i],stackk[j],stackk[k]))<fabs(cross(stackk[i],stackk[j],stackk[(k+1)%n]))) k=(k+1)%n; while(j!=i&&k!=i) { ans=max(ans,fabs(cross(stackk[i],stackk[j],stackk[k]))); while(fabs(cross(stackk[i],stackk[j],stackk[k]))<fabs(cross(stackk[i],stackk[j],stackk[(k+1)%n]))) k=(k+1)%n; j=(j+1)%n; } } return ans*0.5; } int main() { int n; while(~scanf("%d",&n)) { if(n==-1) break; for(int i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); if(n<3) { puts("0.00"); continue; } Graham(n); printf("%.2f\n",rotating_calipers(top)); } return 0; }

    第四题(凸包+ 旋转卡壳求平面最远点对)

    题目传送门POJ 2187 Beauty Contest 题意: 求平面最远点对的距离!!! 直接凸包+旋转卡壳

    #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<math.h> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define PI 3.14159265358979323846 using namespace std; typedef long long int ll; typedef unsigned long long int ull; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } typedef double elem_t; const int N=50005; struct Point { elem_t x,y; }; Point stackk[N]; //凸包中所有点,下标从0....top开始 Point p[N]; //存入的所有点 Point MinA; int top; double dist(Point A,Point B) { return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)); } //计算叉积 判断bc向量到ac向量 是否通过左转得到 >0左转 <0右转 =0共线 elem_t cross(Point A,Point B,Point C) { return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x); } bool cmp(Point a,Point b) //极角排序 { double k=cross(MinA,a,b); if(k>0) return 1; if(k<0) return 0; return dist(MinA,a)<dist(MinA,b); } void Graham(int n) { int i; for(i=1; i<n; i++) if(p[i].y<p[0].y||(p[i].y==p[0].y&&p[i].x<p[0].x)) swap(p[i],p[0]); MinA=p[0]; sort(p+1,p+n,cmp); stackk[0]=p[0]; stackk[1]=p[1]; top=1; for(i=2; i<n; i++) { //注意这里我们把共线的点也压入凸包里 //是否满足叉乘大于0 不满足出栈 控制<0或<=0可以控制重点,共线 while(cross(stackk[top-1],stackk[top],p[i])<=0&&top>=1) --top; stackk[++top]=p[i]; } top++; } //旋转卡壳求凸包的直径,平面最远的点对 // double也可以过 elem_t dis2(Point a, Point b) { return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y); } elem_t rotating_calipers_dist(int top) { int q=1; elem_t ans=0; stackk[top]=stackk[0]; for(int p=0; p<=top; p++) { while(cross(stackk[p+1],stackk[q],stackk[p])<cross(stackk[p+1],stackk[q+1],stackk[p])) q=(q+1)%top; ans=max(ans,max(dis2(stackk[p],stackk[q]),dis2(stackk[p+1],stackk[q+1]))); } return ans; } int main() { int n; while(~scanf("%d",&n)) { if(n==0) break; for(int i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); Graham(n); printf("%.0f\n",rotating_calipers_dist(top)); } return 0; }

    第五题(凸包+ 求凸多边形的面积)

    题目传送门POJ 3348 Cows 题意: 就是让你求下凸包,然后求凸边行的面积,可以用叉积,但是公式一定要对… 我就是这个area_sum()一直理解错了…

    AC代码

    #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<math.h> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define PI 3.14159265358979323846 using namespace std; typedef long long int ll; typedef unsigned long long int ull; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } typedef double elem_t; const int N=50005; struct Point { elem_t x,y; }; Point stackk[N]; //凸包中所有点,下标从0....top开始 Point p[N]; //存入的所有点 Point MinA; int top; double dist(Point A,Point B) { return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)); } //计算叉积 判断bc向量到ac向量 是否通过左转得到 >0左转 <0右转 =0共线 elem_t cross(Point A,Point B,Point C) { return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x); } bool cmp(Point a,Point b) //极角排序 { double k=cross(MinA,a,b); if(k>0) return 1; if(k<0) return 0; return dist(MinA,a)<dist(MinA,b); } void Graham(int n) { int i; for(i=1; i<n; i++) if(p[i].y<p[0].y||(p[i].y==p[0].y&&p[i].x<p[0].x)) swap(p[i],p[0]); MinA=p[0]; sort(p+1,p+n,cmp); stackk[0]=p[0]; stackk[1]=p[1]; top=1; for(i=2; i<n; i++) { //注意这里我们把共线的点也压入凸包里 //是否满足叉乘大于0 不满足出栈 控制<0或<=0可以控制重点,共线 while(cross(stackk[top-1],stackk[top],p[i])<=0&&top>=1) --top; // 这里判断稳定凸包不能有等号 也就是这个共线的情况必须算进去 stackk[++top]=p[i]; } top++; } double area_sum(Point sta[], int n){ // 求凸多边形面积 n是凸包大小 double ans = 0.0; int i = 0; for( i = 1; i < n-1; i++){ ans +=fabs(cross(sta[i],sta[i+1],sta[0])/2.0); } return ans; } int main() { int n; int t; //scanf("%d",&t); while(scanf("%d",&n)!=EOF&&n) { for(int i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); Graham(n); printf("%d\n",(int )area_sum(stackk,top)/50); } return 0; }

    第六题(凸包+最小矩形覆盖)

    题目传送门 P 3187 最小矩形覆盖 题目传送门 bzoj 1185最小矩形覆盖

    题意: 裸的矩形覆盖,就是让你去求一个矩形,覆盖所有的点,然后这个矩阵的面积最小… 然后我真的没有动脑子和手去写…我觉得这个题型已经很死了。。。。所有我拿了别人写好的上了。。。。。

    AC代码

    #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<math.h> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define PI 3.14159265358979323846 using namespace std; const double eps = 1e-8; const int N = 50005; struct point{ double x, y; point operator - (point d) { point dd; dd.x = this -> x - d.x; dd.y = this -> y - d.y; return dd; } point operator + (point d) { point dd; dd.x = this -> x + d.x; dd.y = this -> y + d.y; return dd; } }p[N], ds[N]; int n; int sign(double d) { return d < -eps ? -1 : (d > eps); } double dist(point d1, point d2){ return sqrt(pow(d1.x - d2.x, 2.0) + pow(d1.y - d2.y, 2.0)); } double dist2(point d1, point d2){ return pow(d1.x - d2.x, 2.0) + pow(d1.y - d2.y, 2.0); } bool cmp(point d1, point d2){ return d1.y < d2.y || (d1.y == d2.y && d1.x < d2.x); } //st1 --> ed1 叉乘 st2 --> ed2 的值 double xmul(point st1, point ed1, point st2, point ed2){ return (ed1.x - st1.x) * (ed2.y - st2.y) - (ed1.y - st1.y) * (ed2.x - st2.x); } double dmul(point st1, point ed1, point st2, point ed2){ return (ed1.x - st1.x) * (ed2.x - st2.x) + (ed1.y - st1.y) * (ed2.y - st2.y); } //多边形类 struct poly{ static const int N = 4005; //点数的最大值 point ps[N + 5]; //逆时针储存多边形的点,[0, pn - 1] int pn; // 点数 poly() { pn = 0; } void push(point tp) { // 加入一个点 ps[pn++] = tp; } int trim(int k) { //第k个位置 return (k + pn) % pn; } void clear() { pn = 0; } }; //返回含有n个点的点集ps的凸包 poly graham(point* ps, int n){ sort(ps, ps + n, cmp); poly ans; if(n <= 2){ for(int i = 0; i < n; i++){ ans.push(ps[i]); } return ans; } ans.push(ps[0]); ans.push(ps[1]); point* tps = ans.ps; int top = -1; tps[++top] = ps[0]; tps[++top] = ps[1]; for(int i = 2; i < n; i++){ while(top > 0 && xmul(tps[top - 1], tps[top], tps[top - 1], ps[i]) <= 0) top--; tps[++top] = ps[i]; } int tmp = top; for(int i = n - 2; i >= 0; i--){ while(top > tmp && xmul(tps[top - 1], tps[top], tps[top - 1], ps[i]) <= 0) top--; tps[++top] = ps[i]; } ans.pn = top; return ans; } //求点p到st->ed的垂足,列参数方程 point getRoot(point p, point st, point ed){ point ans; double u=((ed.x-st.x)*(ed.x-st.x)+(ed.y-st.y)*(ed.y-st.y)); u = ((ed.x-st.x)*(ed.x-p.x)+(ed.y-st.y)*(ed.y-p.y))/u; ans.x = u*st.x+(1-u)*ed.x; ans.y = u*st.y+(1-u)*ed.y; return ans; } //next为直线(st,ed)上的点,返回next沿(st,ed)右手垂直方向延伸l之后的点 point change(point st, point ed, point next, double l){ point dd; dd.x = -(ed - st).y; dd.y = (ed - st).x; double len = sqrt(dd.x * dd.x + dd.y * dd.y); dd.x /= len, dd.y /= len; dd.x *= l, dd.y *= l; dd = dd + next; return dd; } //求含n个点的点集ps的最小面积矩形,并把结果放在ds(ds为一个长度是4的数组即可,ds中的点是逆时针的)中,并返回这个最小面积。 double getMinAreaRect(point* ps, int n, point* ds){ int cn, i; double ans; point* con; poly tpoly = graham(ps, n); con = tpoly.ps; cn = tpoly.pn; if(cn <= 2){ ds[0] = con[0]; ds[1] = con[1]; ds[2] = con[1]; ds[3] = con[0]; ans=0; }else{ int l, r, u; double tmp, len; con[cn] = con[0]; ans = 1e40; l = i = 0; while(dmul(con[i], con[i+1], con[i], con[l]) >= dmul(con[i], con[i+1], con[i], con[(l-1+cn)%cn])){ l = (l-1+cn)%cn; } for(r=u=i = 0; i < cn; i++){ while(xmul(con[i], con[i+1], con[i], con[u]) <= xmul(con[i], con[i+1], con[i], con[(u+1)%cn])){ u = (u+1)%cn; } while(dmul(con[i], con[i+1], con[i], con[r]) <= dmul(con[i], con[i+1], con[i], con[(r+1)%cn])){ r = (r+1)%cn; } while(dmul(con[i], con[i+1], con[i], con[l]) >= dmul(con[i], con[i+1], con[i], con[(l+1)%cn])){ l = (l+1)%cn; } tmp = dmul(con[i], con[i+1], con[i], con[r]) - dmul(con[i], con[i+1], con[i], con[l]); tmp *= xmul(con[i], con[i+1], con[i], con[u]); tmp /= dist2(con[i], con[i+1]); len = xmul(con[i], con[i+1], con[i], con[u])/dist(con[i], con[i+1]); if(sign(tmp - ans) < 0){ ans = tmp; ds[0] = getRoot(con[l], con[i], con[i+1]); ds[1] = getRoot(con[r], con[i+1], con[i]); ds[2] = change(con[i], con[i+1], ds[1], len); ds[3] = change(con[i], con[i+1], ds[0], len); } } } return ans + eps; } // P 3187 // double 求最小的面积 输出点 要求第一行为y坐标最小的顶点, //其后按逆时针输出顶点坐标.如果用相同y坐标,先输出最小x坐标的顶点 void solve1(){ scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%lf%lf", &p[i].x, &p[i].y); printf("%.5f\n",getMinAreaRect(p, n, ds)+eps); for(int i = 0; i < 4; i++ ){ printf("%.5f %.5f\n",ds[i].x+eps,ds[i].y+eps); } } // HDU5251 输出矩形的面积为整数 int cas = 1; void solve2(){ scanf("%d", &n); n = n * 4; for (int i = 0; i < n; i++) scanf("%lf%lf", &p[i].x, &p[i].y); printf("Case #%d:\n%d\n", cas++, (int)(getMinAreaRect(p, n, ds) + 0.5)); } int main() { int t; t = 1; scanf("%d", &t); while (t--) { //solve2(); solve1(); } return 0; }

    持续更新中…

    最新回复(0)