[BZOJ1185][HNOI2007]最小矩形覆盖(旋转卡壳)
来源:互联网 发布:网络导购员是做什么的 编辑:程序博客网 时间:2024/05/19 21:00
=== ===
这里放传送门
=== ===
题解
这题好!踏!马!难!写!啊。。
可以发现最小覆盖矩形一定有一条边在凸包上,那么如果按顺序枚举凸包上的一条边,可以用旋转卡壳求出和它平行并且也卡在凸包上的另一条边;对于矩形的另外两条边,可以发现随着按顺序的枚举,它们两个也是跟着一起转的。那么从第一个点开始枚举点
分别在两条直线如何找到跟它们两个垂直并且能够覆盖所有点的直线呢?显然我们应该在某一个范围内枚举点,比如说如果要求C这条直线的位置,我们就应该按照逆时针
然后就剩下一个要求就是这个直线和A,B组成的矩形一定要覆盖所有点。那这个怎么办呢?从图上我们可以发现一个很有用的性质就是C和A交点的横坐标在旋转的过程中是单峰的,在恰好覆盖所有点的时候取最值。
那到底是最大值还是最小值呢。。。。。一开始以为C就是要最大值D就是要最小值,实际上当
代码
#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>#define inc(x)((x==N)?1:x+1)using namespace std;const double eps=1e-10;const double inf=1e60;int n,m,k;double ans;struct Vector{ double x,y; Vector (double X=0,double Y=0){x=X;y=Y;} Vector operator + (const Vector &a){return Vector(x+a.x,y+a.y);} Vector operator - (const Vector &a){return Vector(x-a.x,y-a.y);} double operator * (const Vector &a){return x*a.y-y*a.x;} Vector mul(double t){return Vector(x*t,y*t);} void read(){scanf("%lf%lf",&x,&y);} void print(){printf("%.5lf %.5lf\n",x,y);}}p[50010],c[50010],a1,a2,a3,a4;struct Line{ Vector P,v; Line(){P=Vector();v=Vector();} void get(Vector A,Vector B){P=A;v=B-A;}};int comp(Vector a,Vector b){return a.x<b.x||(a.x==b.x&&a.y>b.y);}//注意点的排序Vector GLI(Line a,Line b){ Vector u=a.P-b.P; double t=(b.v*u)/(a.v*b.v); return a.P+a.v.mul(t);}void ConvexHull(){ sort(p+1,p+n+1,comp); for (int i=1;i<=n;i++){ while (m>1&&(c[m]-c[m-1])*(p[i]-c[m-1])<eps) --m; c[++m]=p[i]; } k=m; for (int i=n-1;i>=1;i--){ while (m>k&&(c[m]-c[m-1])*(p[i]-c[m-1])<eps) --m; c[++m]=p[i]; } if (n>1) --m;}bool better(Vector a,Vector b){ return (b.y-a.y>eps)||(fabs(b.y-a.y)<eps&&b.x-a.x>eps);}void Update(Line A,Line B,Vector P,Vector Q){ Line C,D; Vector t[5]; double ar; int rec; C.v=D.v=Vector(-A.v.y,A.v.x); C.P=P;D.P=Q; t[1]=GLI(C,A);t[2]=GLI(A,D); t[3]=GLI(D,B);t[4]=GLI(B,C); ar=fabs((t[2]-t[1])*(t[3]-t[1]))+fabs((t[3]-t[1])*(t[4]-t[1])); ar/=2; if (ans-ar>eps){ ans=ar;rec=1; for (int i=2;i<=4;i++) if (better(t[i],t[rec])) rec=i; a1=t[rec];rec=rec%4+1; a2=t[rec];rec=rec%4+1; a3=t[rec];rec=rec%4+1; a4=t[rec];rec=rec%4+1; }}bool check(Line A,int now,int opt){ Line T1,T2; Vector ip1,ip2; T1.P=c[now];T1.v=Vector(-A.v.y,A.v.x); T2.P=c[now+1];T2.v=T1.v; ip1=GLI(T1,A);ip2=GLI(T2,A); if (opt==0) return (ip1.x-ip2.x>eps||(fabs(ip1.x-ip2.x)<=eps&&ip2.y-ip1.y>eps)); else return (ip2.x-ip1.x>eps||(fabs(ip1.x-ip2.x)<eps&&ip1.y-ip2.y>eps));}void Rotating_Calipers(int N){ if (N==1){ans=0;a1=a2=a3=a4=c[1];return;} if (N==2){ ans=0; if (better(c[2],c[1])) swap(c[1],c[2]); a1=a2=c[1];a3=a4=c[2]; return; } int p1=1,p2=inc(p1),p3,p4,r0=0,r1=1; Line A,B; while (p1<=N){ while ((c[p1+1]-c[p1])*(c[p2+1]-c[p2])>eps) p2=inc(p2);//找到能被当前这条边卡住的点 A.get(c[p1],c[p1+1]); B.get(c[p2],c[p2]+A.v); if (p1==1){p3=p2;p4=p1;}//对另外两个点进行初始化 if (p1==k) swap(r0,r1);//注意当旋转到上凸壳以后判断条件要更改 while (p3!=p1&&check(A,p3,r0)) p3=inc(p3); while (p4!=p2&&check(A,p4,r1)) p4=inc(p4); Update(A,B,c[p3],c[p4]); ++p1; }}int main(){ scanf("%d",&n); for (int i=1;i<=n;i++) p[i].read(); ConvexHull(); ans=inf; Rotating_Calipers(m); printf("%.5lf\n",ans); a1.print();a2.print();a3.print();a4.print(); return 0;}
偏偏在最后出现的补充说明
旋转卡壳本质上就是利用了凸包上的单调性。
凸包上有很多很多单调性哟
0 0
- BZOJ1185 [HNOI2007]最小矩形覆盖(旋转卡壳)
- [BZOJ1185][HNOI2007]最小矩形覆盖(凸包+旋转卡壳)
- [BZOJ1185][HNOI2007]最小矩形覆盖(旋转卡壳)
- bzoj1185: [HNOI2007]最小矩形覆盖 计算几何 旋转卡壳
- bzoj1185[HNOI2007]最小矩形覆盖
- bzoj1185【HNOI2007】最小矩形覆盖
- 【HNOI2007】bzoj1185 最小矩形覆盖
- bzoj 1185: [HNOI2007]最小矩形覆盖 (旋转卡壳)
- 【BZOJ1185】最小矩形覆盖 计算几何 凸包 旋转卡壳
- BZOJ 1185 HNOI2007 最小矩形覆盖 旋转卡壳
- BZOJ 1185 [HNOI2007]最小矩形覆盖 旋转卡壳
- bzoj 1185: [HNOI2007]最小矩形覆盖 旋转卡壳
- 【BZOJ 1185】[HNOI2007]最小矩形覆盖 旋转卡壳
- BZOJ 1185([HNOI2007]最小矩形覆盖-旋转卡壳+点集几何意义)
- [旋转卡壳] BZOJ 1185 [HNOI2007]最小矩形覆盖 && 2218 Uva10173 Smallest Bounding Rectangle
- 最小矩形覆盖(凸包旋转卡壳)
- UVA 10173 最小矩形覆盖(凸包+旋转卡壳)
- 1185: [HNOI2007]最小矩形覆盖
- 视频直播
- php循环练习
- RecycleView.Adapt的简单封装
- 使用logback
- php实现的几种基本算法
- [BZOJ1185][HNOI2007]最小矩形覆盖(旋转卡壳)
- 如何开启Spring Boot的开发模式?
- 冒泡排序BubbleSort
- git rebase
- 使用Gradle生成library工程的jar包
- 利用SharedPreference 保存List<Object>集合
- 北邮OJ-100. 二叉树的层数-12网研上机B
- 安卓下拉刷新开源库对比
- 类的继承