BZOJ1185[HNOI2007] 最小矩形覆盖

题面

给定一些点的坐标,要求求能够覆盖所有点的最小面积的矩形,输出所求矩形的面积和四个顶点的坐标

分析

首先可以先求凸包,因为覆盖了凸包上的顶点,凸包内的顶点也一定能被覆盖

结论:这个矩形的一条边一定与凸包的一条边重合。

然后对于凸包的每一条边\(\vec{s_is_{i+1}}\),我们通过旋转卡壳找到最左侧的点l,最右侧的点r,最高点p,过p做\(\vec{s_is_{i+1}}\)的平行线,过l,r做\(\vec{s_is_{i+1}}\)的垂线,就得到了我们要求的矩形

求最大高度的点用叉积最大,最右侧点用点积最大,最左侧点用点积最小(点积为负)

求坐标,可以先根据前面的点积和叉积求出投影长度,再求出边对应的单位向量,乘上长度再相加就得到了点的坐标。如l的坐标就是$s_i+ \frac{\vec{s_is_{i+1}} \cdot \vec{s_il}{}}{|\vec{s_is_{i+1}}|} \frac{ \vec{s_is_{i+1}} }{|\vec{s_is_{i+1}}|} $,另两个端点只需要向量旋转90度即可

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 100000
#define eps 1e-10
#define INF 1e20
using namespace std;
int n;
struct Vector{
double x;
double y;
Vector(){ }
Vector(double _x,double _y){
x=_x;
y=_y;
}
friend Vector operator + (Vector p,Vector q){
return Vector(p.x+q.x,p.y+q.y);
}
friend Vector operator - (Vector p,Vector q){
return Vector(p.x-q.x,p.y-q.y);
}
friend Vector operator * (Vector a,double d){
return Vector(a.x*d,a.y*d);
}
friend Vector operator / (Vector a,double d){
return Vector(a.x/d,a.y/d);
}
};
typedef Vector point;
inline double dot(Vector p,Vector q){
return p.x*q.x+p.y*q.y;
}
inline double dist(point p,point q){
return sqrt(dot(p-q,p-q));
}
inline double cross(Vector p,Vector q){
return p.x*q.y-p.y*q.x;
}
inline double length(Vector x){
return sqrt(dot(x,x));
}
point a[maxn+5]; point O;
int cmp(point P,point Q){
double ang=cross(P-O,Q-O);
if(fabs(ang)<eps) return dist(O,P)<dist(O,Q);
else return ang>eps;
} int top=0;
point s[maxn+5];
void Graham(){
for(int i=1;i<=n;i++){
if(a[i].x<a[1].x||(a[i].x==a[1].x&&a[i].y<=a[1].y)) swap(a[i],a[1]);
}
O=a[1];
sort(a+2,a+1+n,cmp);
for(int i=1;i<=n;i++){
while(top>1&&cross(s[top]-s[top-1],a[i]-s[top-1])<=eps) top--;//在逆时针方向,叉积<0
s[++top]=a[i];
}
//#ifdef DEBUG
// for(int i=1;i<=top;i++){
// printf("(%.5f,%.5f)\n",s[i].x,s[i].y);
// }
//#endif
} inline int nex(int x){
return x%top+1;
}
inline Vector rotate90(Vector p){//把向量逆时针旋转90度
//(xcos(a)-ysin(a),xsin(a)+ycos(a)) a=pi/2
return Vector(-p.y,p.x);
} point res[10];
double ans=INF;
void Spin(){
s[top+1]=s[1];
int l,r,p;
l=r=p=2;
for(int i=1;i<=top;i++){
double D=length(s[i+1]-s[i]);
while(cross(s[i+1]-s[i],s[p+1]-s[i])-cross(s[i+1]-s[i],s[p]-s[i])>-eps) p=nex(p);
while(dot(s[r+1]-s[i],s[i+1]-s[i])-dot(s[r]-s[i],s[i+1]-s[i])>-eps) r=nex(r);
if(i==1) l=r;
while(dot(s[l+1]-s[i],s[i+1]-s[i])-dot(s[l]-s[i],s[i+1]-s[i])<eps) l=nex(l);
//在左侧的时候点积为负数,最小
double lenl=dot(s[l]-s[i],s[i+1]-s[i])/D;
double lenr=dot(s[r]-s[i],s[i+1]-s[i])/D;
double height=cross(s[i+1]-s[i],s[p]-s[i])/D;
double area=(fabs(lenr)+fabs(lenl))*fabs(height);//lenl,lenr是有方向的
if(area<ans){
// printf("(%.3f %.3f) ",(double)s[i].x,(double)s[i].y);
// printf("(%.3f %.3f)\n",(double)s[i+1].x,(double)s[i+1].y);
// printf("S=%.4f l=%d r=%d p=%d\n",area,l,r,p);
ans=area;
res[1]=s[i]+(s[i+1]-s[i])/D*lenl;
res[2]=s[i]+(s[i+1]-s[i])/D*lenr;
res[3]=res[1]+rotate90((s[i+1]-s[i])/D)*height;
res[4]=res[2]+rotate90((s[i+1]-s[i])/D)*height;
}
}
} int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf %lf",&a[i].x,&a[i].y);
}
Graham();
Spin();
for(int i=1;i<=4;i++){
if(res[i].y<res[1].y||(res[i].y==res[1].y&&res[i].x<res[1].x)) swap(res[1],res[i]);
}
O=res[1];
sort(res+2,res+1+4,cmp);
for(int i=1;i<=4;i++){
if(fabs(res[i].x)<eps) res[i].x=0;
if(fabs(res[i].y)<eps) res[i].y=0;
}
printf("%.5f\n",ans);
for(int i=1;i<=4;i++){
printf("%.5f %.5f\n",res[i].x,res[i].y);
}
}

BZOJ1185[HNOI2007] 最小矩形覆盖(旋转卡壳)的更多相关文章

  1. bzoj1185 [HNOI2007]最小矩形覆盖 旋转卡壳求凸包

    [HNOI2007]最小矩形覆盖 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 2081  Solved: 920 ...

  2. BZOJ 1185: [HNOI2007]最小矩形覆盖 [旋转卡壳]

    1185: [HNOI2007]最小矩形覆盖 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 1435  Solve ...

  3. 洛谷 P3187 BZOJ 1185 [HNOI2007]最小矩形覆盖 (旋转卡壳)

    题目链接: 洛谷 P3187 [HNOI2007]最小矩形覆盖 BZOJ 1185: [HNOI2007]最小矩形覆盖 Description 给定一些点的坐标,要求求能够覆盖所有点的最小面积的矩形, ...

  4. 【bzoj1185】[HNOI2007]最小矩形覆盖 (旋转卡壳)

    给你一些点,让你用最小的矩形覆盖这些点 首先有一个结论,矩形的一条边一定在凸包上!!! 枚举凸包上的边 用旋转卡壳在凸包上找矩形另外三点... 注意精度问题 #include<cstdio> ...

  5. bzoj 1185 [HNOI2007]最小矩形覆盖——旋转卡壳

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1185 矩形一定贴着凸包的一条边.不过只是感觉这样. 枚举一条边,对面的点就是正常的旋转卡壳. ...

  6. BZOJ 1185: [HNOI2007]最小矩形覆盖-旋转卡壳法求点集最小外接矩形(面积)并输出四个顶点坐标-备忘板子

    来源:旋转卡壳法求点集最小外接矩形(面积)并输出四个顶点坐标 BZOJ又崩了,直接贴一下人家的代码. 代码: #include"stdio.h" #include"str ...

  7. BZOJ1185 [HNOI2007]最小矩形覆盖 【旋转卡壳】

    题目链接 BZOJ1185 题解 最小矩形一定有一条边在凸包上,枚举这条边,然后旋转卡壳维护另外三个端点即可 计算几何细节极多 维护另外三个端点尽量不在这条边上,意味着左端点尽量靠后,右端点尽量靠前, ...

  8. 2018.10.18 bzoj1185: [HNOI2007]最小矩形覆盖(旋转卡壳)

    传送门 不难看出最后的矩形一定有一条边与凸包某条边重合. 因此先求出凸包,然后旋转卡壳求出当前最小矩形面积更新答案. 代码: #include<bits/stdc++.h> #define ...

  9. BZOJ1185 HNOI2007 最小矩形覆盖 凸包、旋转卡壳

    传送门 首先,肯定只有凸包上的点会限制这个矩形,所以建立凸包. 然后可以知道,矩形上一定有一条边与凸包上的边重合,否则可以转一下使得它重合,答案会更小. 于是沿着凸包枚举这一条边,通过旋转卡壳找到离这 ...

随机推荐

  1. bzoj5020 & loj2289 [THUWC 2017]在美妙的数学王国中畅游 LCT + 泰勒展开

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5020 https://loj.ac/problem/2289 题解 这个 appear 和 d ...

  2. os模块、sys模块、json模块、pickle模块、logging模块

    目录 os模块 sys模块 json模块 pickle模块 logging模块 os模块 功能:与操作系统交互,可以操作文件 一.对文件操作 判断是否为文件 os.path.isfile(r'路径') ...

  3. javaScript中的 call 和 apply

    call 和apply都可以实现函数的调用 // 普通函数的调用 function foo() { console.log('foo'); } foo(); // foo foo.call(); // ...

  4. metaclass简单使用

    metaclass : 元类, 就是创建python类对象的类 # 便用type动态创建类 # 1. 使用type创建一个User类对象 无基类,无属性 . () : 表示基类, {}:属性 User ...

  5. linux运维、架构之路-Lamp架构部署

    一.Lamp架构原理 二.Lamp架构软件安装 1.apache安装脚本 #!/bin/sh cd /server/tools/ yum install zlib-devel -y wget http ...

  6. 【bzoj2733】[HNOI2012]永无乡

    题目描述: 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到 ...

  7. Software-Defined Networking: A Comprehensive Survey

    文章名称:Software-Defined Networking: A Comprehensive Survey 文章来源:Proceedings of the IEEE ( Volume: 103  ...

  8. Spring Cloud教程(十二)加密和解密

    Spring Cloud具有一个用于在本地解密属性值的Environment预处理器.它遵循与Config Server相同的规则,并通过encrypt.*具有相同的外部配置.因此,您可以使用{cip ...

  9. axios的详细用法以及后端接口代理

    安装 使用 npm: $ npm install axios 或者 使用 bower: $ bower install axios 或者直接使用 cdn: <script src="h ...

  10. 2019 Nanchang Onsite

    D.Interesting Series F(n)实际上是一个等比数列的和,将它从递推式转变为通项公式(a^n-1)/(a-1),这里只需要确定n就可以. 题目要求选取k大小的所有子集的答案求和,可以 ...