【BZOJ1185】[HNOI2007]最小矩形覆盖(凸包,旋转卡壳)
【BZOJ1185】[HNOI2007]最小矩形覆盖(凸包,旋转卡壳)
题面
题解
最小的矩形一定存在一条边在凸包上,那么枚举这条边,我们还差三个点,即距离当前边的最远点,以及做这条边的垂线的最靠左和最靠右的两个点。
最远点很容易求,叉积计算面积来比就好了。
那么剩下两个点呢?
比如说找右侧的那个点,我们假装当前枚举出来的这条边就是水平线,那么只要当前的点和下一个点的直线与\(x\)轴正半轴夹角小于\(90°\) 显然就往这个方向走。然后从水平线换到一般的情况,也就是和枚举的这条边的夹角小于\(\frac{\pi}{2}\),点积的计算除了坐标计算之外,还有\(\vec{a}\dot{}\vec{b}=|\vec{a}|*|\vec{b}|*cos<\vec{a},\vec{b}>\),这样子可以很容易求出两个向量之间的夹角关系,而\(\alpha\le \frac{\pi}{2}\)换成三角函数之间的关系就是\(cos\alpha\ge 0\),因此找这个关系只需要很简单的判断两个向量之间的点积是否大于等于\(0\)。
同理,考虑如何找最靠左的点,那么就是两个向量的夹角范围在\(\frac{\pi}{2}\)以上,即点积小于\(0\)。
通过这个方法,似乎可以求解已知所有夹角的\(n\)边形覆盖,只需要旋转卡壳的时候依次考虑每个点,而每个点是否能否移动到下个点的条件与夹角相关,而夹角的信息可以通过点积得到。
然后这题就是卡精度卡精度卡精度之类的
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define double long double
#define MAX 50050
const double eps=1e-10;
const double Pi=acos(-1);
struct Point{double x,y,ang;};
bool operator<(Point a,Point b){return (a.ang!=b.ang)?a.ang<b.ang:a.x<b.x;}
Point operator+(Point a,Point b){return (Point){a.x+b.x,a.y+b.y};}
Point operator-(Point a,Point b){return (Point){a.x-b.x,a.y-b.y};}
Point operator*(Point a,double b){return (Point){a.x*b,a.y*b};}
Point operator/(Point a,double b){return (Point){a.x/b,a.y/b};}
double operator*(Point a,Point b){return a.x*b.x+a.y*b.y;}
double Cross(Point a,Point b){return a.x*b.y-a.y*b.x;}
double Len(Point a){return sqrt(a.x*a.x+a.y*a.y);}
double Dis(Point a,Point b){return Len(a-b);}
Point Rotate(Point p,double a){double c=cos(a),s=sin(a);return (Point){p.x*c-p.y*s,p.x*s+p.y*c};}
Point S[MAX],Ans[10];int top;
void Graham(Point *p,int n)
{
int pos=1;
for(int i=2;i<=n;++i)
if(p[i].x<p[pos].x||(p[i].x==p[pos].x&&p[i].y<p[pos].y))
pos=i;
swap(p[1],p[pos]);
for(int i=2;i<=n;++i)p[i].ang=atan2(p[i].y-p[1].y,p[i].x-p[1].x);
sort(&p[2],&p[n+1]);S[++top]=p[1];S[++top]=p[2];
for(int i=3;i<=n;++i)
{
while(top>2&&Cross(p[i]-S[top],p[i]-S[top-1])>=0)--top;
S[++top]=p[i];
}
}
struct Line{Point a,v;};
Point Intersection(Line a,Line b)
{
Point c=b.a-a.a;
double t=Cross(b.v,c)/Cross(b.v,a.v);
return a.a+a.v*t;
}
int n;double ans=1e18;
Point p[MAX],tmp[5];
void ScanLine(int n)
{
S[n+1]=S[1];S[0]=S[n];
for(int i=1,j1=3,j2=3,j3=n;i<=n;++i)
{
if(i==1)while((S[i]-S[i+1])*(S[j3-1]-S[j3])>0)j3=(j3==1)?n:j3-1;
while(Cross(S[j1]-S[i],S[j1]-S[i+1])<=Cross(S[j1+1]-S[i],S[j1+1]-S[i+1]))j1=(j1==n)?1:j1+1;
while((S[i+1]-S[i])*(S[j2+1]-S[j2])>0)j2=(j2==n)?1:j2+1;
while((S[i+1]-S[i])*(S[j3+1]-S[j3])<0)j3=(j3==n)?1:j3+1;
Line l0=(Line){S[i],S[i+1]-S[i]};
Line l1=(Line){S[j1],S[i]-S[i+1]};
Line l2=(Line){S[j2],Rotate(S[i+1]-S[i],Pi/2)};
Line l3=(Line){S[j3],Rotate(S[i]-S[i+1],Pi/2)};
tmp[1]=Intersection(l0,l2);
tmp[2]=Intersection(l2,l1);
tmp[3]=Intersection(l1,l3);
tmp[4]=Intersection(l3,l0);
double area=Dis(tmp[1],tmp[2])*Dis(tmp[2],tmp[3]);
if(area<ans)
ans=area,Ans[1]=tmp[1],Ans[2]=tmp[2],Ans[3]=tmp[3],Ans[4]=tmp[4];
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%Lf%Lf",&p[i].x,&p[i].y);
for(int i=1;i<=n;++i)p[i].x+=eps,p[i].y-=eps;
Graham(p,n);
ScanLine(top);
printf("%.5Lf\n",ans);
Ans[5]=Ans[1];Ans[6]=Ans[2];Ans[7]=Ans[3];Ans[8]=Ans[4];
int pos=1;
for(int i=2;i<=4;++i)
if(Ans[i].y<Ans[pos].y||(Ans[i].y==Ans[pos].y&&Ans[i].x<=Ans[pos].x))
pos=i;
for(int i=pos;i<=pos+3;++i)printf("%.5Lf %.5Lf\n",Ans[i].x+100*eps,Ans[i].y+100*eps);
return 0;
}
【BZOJ1185】[HNOI2007]最小矩形覆盖(凸包,旋转卡壳)的更多相关文章
- [BZOJ1185][HNOI2007]最小矩形覆盖-[凸包+旋转卡壳]
Description 传送门 Solution 感性理解一下,最小矩形一定是由一条边和凸包上的边重合的. 然后它就是模板题了..然而真的好难调,小于大于动不动就打错. Code #include&l ...
- BZOJ1185[HNOI2007] 最小矩形覆盖(旋转卡壳)
BZOJ1185[HNOI2007] 最小矩形覆盖 题面 给定一些点的坐标,要求求能够覆盖所有点的最小面积的矩形,输出所求矩形的面积和四个顶点的坐标 分析 首先可以先求凸包,因为覆盖了凸包上的顶点,凸 ...
- BZOJ1185 [HNOI2007]最小矩形覆盖 【旋转卡壳】
题目链接 BZOJ1185 题解 最小矩形一定有一条边在凸包上,枚举这条边,然后旋转卡壳维护另外三个端点即可 计算几何细节极多 维护另外三个端点尽量不在这条边上,意味着左端点尽量靠后,右端点尽量靠前, ...
- bzoj 1185 [HNOI2007]最小矩形覆盖 凸包+旋转卡壳
题目大意 用最小矩形覆盖平面上所有的点 分析 有一结论:最小矩形中有一条边在凸包的边上,不然可以旋转一个角度让面积变小 简略证明 我们逆时针枚举一条边 用旋转卡壳维护此时最左,最右,最上的点 注意 注 ...
- 2018.10.18 bzoj1185: [HNOI2007]最小矩形覆盖(旋转卡壳)
传送门 不难看出最后的矩形一定有一条边与凸包某条边重合. 因此先求出凸包,然后旋转卡壳求出当前最小矩形面积更新答案. 代码: #include<bits/stdc++.h> #define ...
- [HNOI2007][BZOJ1185] 最小矩形覆盖 [凸包+旋转卡壳]
题面 BZOJ题面 前置芝士 建议先学习向量相关的计算几何基础 计算几何基础戳这里 思路 用这道题学习一下凸包和旋转卡壳 首先是凸包部分 凸包 求凸包用的算法是graham算法 算法流程如下: 找到$ ...
- BZOJ1185 HNOI2007 最小矩形覆盖 凸包、旋转卡壳
传送门 首先,肯定只有凸包上的点会限制这个矩形,所以建立凸包. 然后可以知道,矩形上一定有一条边与凸包上的边重合,否则可以转一下使得它重合,答案会更小. 于是沿着凸包枚举这一条边,通过旋转卡壳找到离这 ...
- bzoj1185 [HNOI2007]最小矩形覆盖 旋转卡壳求凸包
[HNOI2007]最小矩形覆盖 Time Limit: 10 Sec Memory Limit: 162 MBSec Special JudgeSubmit: 2081 Solved: 920 ...
- BZOJ1185 : [HNOI2007]最小矩形覆盖
求出凸包后,矩形的一条边一定与凸包的某条边重合. 枚举每条边,求出离它最远的点和离它最左最右的点,因为那三个点是单调变化的,所以复杂度为$O(n)$. 注意精度. #include<cstdio ...
- bzoj千题计划209:bzoj1185: [HNOI2007]最小矩形覆盖
http://www.lydsy.com/JudgeOnline/problem.php?id=1185 题解去看它 http://www.cnblogs.com/TheRoadToTheGold/p ...
随机推荐
- python 中的re模块,正则表达式
一.re模块 re模块中常用的方法. match: 默认从字符串开头开始匹配,re.match('fun', 'funny') 可以匹配出来 'fun' match(pattern, string, ...
- Debian搭建WordPress
环境配置 可以使用apt-get快速安装mysql,php5:我是用源码手动安装apache服务器的.安装完mysql后,最好将字符编码设置为utf8的. 接下来就是mysql,apache,php5 ...
- Linux 安装软件之后设置PATH环境变量
每一个软件都有安装路径这一项,指定安装路径的目的,一方面是便于文件搜索与查找,另一方面更方便的使用软件. 比如,几乎大多数自己安装的软件,都会选择安装在/usr/local目录下,比如apache.m ...
- VMware虚拟机与Windows文件共享
开发中,我们经常的需求是这样的:我想再Windows中进行快捷开发,但是想在linux中运行,那么需要将文件方便在linux中管理,基本可以分成两种方式: 1. 使用网络工具:vmware_tool工 ...
- js-其他跨域技术(JSONP`Comet)
###1. JSONP JSONP由两部分组成:回调函数和数据 JSONP是通过动态<script>元素来使用的,使用时可以为src属性指定一个跨域URL eg: function ha ...
- jQuery EasyUI布局容器layout实例精讲
这个布局容器,有五个区域:北.南.东.西和中心. 他中心地区面板是必需的,但是边缘地区面板是可选的.每一个边缘地区面板可以缩放的拖动其边境, 他们也可以通过点击其收缩触发.布局可以嵌套,从而用户可以建 ...
- JS --- 本地保存localStorage、sessionStorage用法总结
JS的本地保存localStorage.sessionStorage用法总结 localStorage.sessionStorage是Html5的特性,IE7以下浏览器不支持 为什么要掌握localS ...
- 字符串正则替换replace第二个参数是函数
zepto中 //将字符串转成驼峰式的格式 camelize = function (str) { return str.replace(/-+(.)?/g, function (match, chr ...
- GlusterFS卷的种类
1.分布卷 在分布式卷中,文件随机扩展到卷中的砖块中. 使用分布式卷,需要扩展存储和冗余不是很重要,或由其他硬件/软件层提供. 创建语法:gluster volume create [transpor ...
- python数据结构与算法第十六天【贪心算法与动态规划】
对于一个字符串,对字符串进行分割,分割后的每个子字符串都为回文串,求解所有可行的方案 这个问题可以使用贪心算法与动态规划来求解 步骤如下: (1)先得出所有的单个字符的回文串,单个字符必定是回文串, ...