题面

BZOJ题面

前置芝士

建议先学习向量相关的计算几何基础

计算几何基础戳这里

思路

用这道题学习一下凸包和旋转卡壳

首先是凸包部分

凸包

求凸包用的算法是graham算法

算法流程如下:

找到$y$坐标最小的一点作为原点

对原点之外的所有点按照到原点的极角排序(这里因为选取了最靠下的,所以极角范围在$[0,\pi]$)

依次遍历所有排序后的点,加入一个单调栈中:每次判断(栈顶元素和栈顶第二元素之间的斜率)是否大于(当前点和栈顶第二元素之间的斜率)

注意一旦这个大于成立了,栈顶元素就会在当前元素和栈顶第二元素的连线的“下面”,也就是在凸包里面了

因为我们事先按照极角排序了,所以这一单调栈可以不重复不遗漏地记录凸包上所有点

注意这样求出来的凸包上的点是逆时针排序的(根本原因是因为极角排序就是逆时针绕圈)

graham算法的复杂度是$O(n\log n)$,瓶颈是排序

旋转卡壳

首先,我在这道题里面用的不是标准的旋转卡壳算法......但是也是“旋转+卡壳”的思路

标准版的旋转卡壳戳这里,这里标准版指的是用4条边去卡壳,我写的是一条边和3个极值点

对于最小面积矩形,我们有结论:这一外接矩形一定有一条边和凸包的一条边重合

注意这个结论对于最小周长矩形依然成立

证明嘛......我愣是没找到。感性理解一下就是对于一个四边都接在凸包端点上的矩形,把它旋转一下一定更优

核心算法流程如下:

我们遍历凸包上的每一条边,并对于每一条边求出以这条边为x轴时,最靠左的点、最靠右的点、最靠上的点

设求出来的凸包上的点是$q[0...m-1]$

假设我们当前的边的两个端点是凸包上的$q[i],q[i+1]$,而且是有向的(i指向i+1),那么上述三个端点有如下性质:

对于最靠左的点$q[l]$,$vec(q[i],q[l])\ast vec(q[i],q[i+1])$是所有$l$中最小的

对于最靠右的点$q[r]$,$vec(q[i],q[r])\ast vec(q[i],q[i+1])$是所有$r$中最大的

对于最靠上的点$q[u]$,$vec(q[i],q[i+1])$叉乘$vec(q[i],q[u])$是所有$u$中最大的

其中$vec(u,v)$表示从点$u$指向$v$的向量

前两个的证明,利用点乘的性质:因为点乘的被投影向量长度相等,所以决定点乘结果大小的就是投影的大小

那么显然投影最小最靠左,投影最大最靠右

第三个的证明,利用叉乘的性质:叉乘等于两个向量逆时针旋转构成的有向平行四边形面积

因为平行四边形底边长度相同,而且$vec(q[i],q[i+1])$一定在所有从$q[i]$出发的向量的顺时针方向,所以反过来旋转一定是正的,叉乘最大就是最高

图示如下:

知道了这三个点以后,我们就可以知道这个矩形的长宽,进而求出面积了

又有性质:我们每次从$vec(q[i],q[i+1])$旋转到$vec(q[i+1],q[i+2])$的时候,$l,r,u$也会跟着逆时针旋转,所以只要枚举即可

这样,整个旋转卡壳就是“遍历所有边”+“顺序求出端点”的过程,总复杂度是$O(n)$的

Code

代码中node是向量结构体,对于点我们用位矢表示,也是向量

标$\ast$的是叉乘,标\的是点乘

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<cmath>
#define eps 1e-9
#define ll long long
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
struct node{
double x,y;
node(double xx=0.0,double yy=0.0){x=xx;y=yy;}
inline bool operator <(const node &b){return ((fabs(y-b.y)<eps)?(x<b.x):(y<b.y));}
inline friend bool operator ==(const node &a,const node &b){return ((fabs(a.x-b.x)<eps)&&(fabs(a.y-b.y)<eps));}
inline friend bool operator !=(const node &a,const node &b){return !(a==b);}
inline friend node operator +(const node &l,const node &r){return node(l.x+r.x,l.y+r.y);}
inline friend node operator -(const node &l,const node &r){return node(l.x-r.x,l.y-r.y);}
inline friend node operator *(node l,double r){return node(l.x*r,l.y*r);}
inline friend double operator *(const node &l,const node &r){return l.x*r.y-l.y*r.x;}
inline friend double operator /(const node &l,const node &r){return l.x*r.x+l.y*r.y;}
inline friend double dis(const node &a){return sqrt(a.x*a.x+a.y*a.y);}
}a[100010],q[100010],x[10];
int n,top;double ans=1e60;
inline bool cmp(node l,node r){
double tmp=(a[1]-l)*(a[1]-r);
if(fabs(tmp)<eps) return dis(a[1]-l)<dis(a[1]-r);
else return tmp>0;
}
void graham(){//get a counter-clockwise convex
int i;
for(i=2;i<=n;i++){
if(a[i]<a[1]) swap(a[1],a[i]);
}
sort(a+2,a+n+1,cmp);
q[++top]=a[1];
q[++top]=a[2];
for(i=3;i<=n;i++){
while(top>1&&((q[top]-q[top-1])*(a[i]-q[top])<eps)) top--;
q[++top]=a[i];
}
q[0]=q[top];
}
void RC(){//RotatingCalipers
int l=1,r=1,p=1,i;
double L,R,D,H,tmp;
for(i=0;i<top;i++){
D=dis(q[i]-q[i+1]);
while((q[i+1]-q[i])*(q[p+1]-q[i])-(q[i+1]-q[i])*(q[p]-q[i])>-eps) p=(p+1)%top;
while((q[i+1]-q[i])/(q[r+1]-q[i])-(q[i+1]-q[i])/(q[r]-q[i])>-eps) r=(r+1)%top;
if(i==0) l=r;
while((q[i+1]-q[i])/(q[l+1]-q[i])-(q[i+1]-q[i])/(q[l]-q[i])<eps) l=(l+1)%top;
L=(q[i+1]-q[i])/(q[l]-q[i])/D;
R=(q[i+1]-q[i])/(q[r]-q[i])/D;
H=(q[i+1]-q[i])*(q[p]-q[i])/D;
tmp=(R-L)*H;
if(tmp<ans){
ans=tmp;
x[0]=q[i]+(q[i+1]-q[i])*(R/D);
x[1]=x[0]+(q[r]-x[0])*(H/dis(x[0]-q[r]));
x[2]=x[1]-(x[0]-q[i])*((R-L)/dis(q[i]-x[0]));
x[3]=x[2]-(x[1]-x[0]);
}
}
}
int main(){
n=read();int i,j;
for(i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y);
graham();
RC();
printf("%.5lf\n",ans);
j=0;
for(i=1;i<4;i++) if(x[i]<x[j]) j=i;
for(i=0;i<4;i++) printf("%.5lf %.5lf\n",x[j].x,x[j].y),j=(j+1)%4;
}

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

  1. bzoj 1185 [HNOI2007]最小矩形覆盖 凸包+旋转卡壳

    题目大意 用最小矩形覆盖平面上所有的点 分析 有一结论:最小矩形中有一条边在凸包的边上,不然可以旋转一个角度让面积变小 简略证明 我们逆时针枚举一条边 用旋转卡壳维护此时最左,最右,最上的点 注意 注 ...

  2. [BZOJ1185][HNOI2007]最小矩形覆盖-[凸包+旋转卡壳]

    Description 传送门 Solution 感性理解一下,最小矩形一定是由一条边和凸包上的边重合的. 然后它就是模板题了..然而真的好难调,小于大于动不动就打错. Code #include&l ...

  3. BZOJ1185[HNOI2007] 最小矩形覆盖(旋转卡壳)

    BZOJ1185[HNOI2007] 最小矩形覆盖 题面 给定一些点的坐标,要求求能够覆盖所有点的最小面积的矩形,输出所求矩形的面积和四个顶点的坐标 分析 首先可以先求凸包,因为覆盖了凸包上的顶点,凸 ...

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

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

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

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

  6. bzoj1185【HNOI2007】最小矩形覆盖

    1185: [HNOI2007]最小矩形覆盖 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special Judge Submit: 1114  Solv ...

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

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

  8. 【BZOJ1185】[HNOI2007]最小矩形覆盖(凸包,旋转卡壳)

    [BZOJ1185][HNOI2007]最小矩形覆盖(凸包,旋转卡壳) 题面 BZOJ 洛谷 题解 最小的矩形一定存在一条边在凸包上,那么枚举这条边,我们还差三个点,即距离当前边的最远点,以及做这条边 ...

  9. 【旋转卡壳+凸包】BZOJ1185:[HNOI2007]最小矩形覆盖

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

随机推荐

  1. NB-IOT连接移动onenet平台流程

    1. 先创建账号,然后创建产品 2. 创建设备,用AT+CGSN和AT+CIMI查询NB-IOT的IMEI和IMSI填写上去. 3. 创建好的设备.

  2. 「日常训练&知识学习」树的直径(POJ-1849,Two)

    题意 一个城市由节点和连接节点的街道组成,街道是双向的. 此刻大雪覆盖了这个城市,市长确定了一些街道要将它们清扫干净,这些街道保证所有的节点可以通过它们连通而且街道数目尽可能小. 现有两台相同的扫雪机 ...

  3. 基于Python的接口自动化

    第一步 Python的安装配置 打开官网: https://www.python.org/downloads/ 目前官网上已经更新到3.6.1啦,有两个版本,大家可以按自己喜欢的去下载,我自己选择的是 ...

  4. 【cookie接口】- jmeter - (请求提示no cookie)

    1.虽然 请求成功 响应码 200  ,但是  返回code 1  ,表示接口不成功 2.加入 空的cookie 管理器就可以了  返回 code 0 注意:状态码 200 只是表示请求是成功的 , ...

  5. clientHeight、offsetHeight、scrollHeight、clientTop、scrollTop、offsetTop的对比

    首先,这些都是dom节点的属性. 高宽属性:clientHeight:html元素不含border的高度. 对于box-sizing不同的情况,有些地方需要注意一下.当box-sizing为conte ...

  6. leetcode个人题解——#11 Container with most water

    class Solution { public: int maxArea(vector<int>& height) { ; ; ; while(l < r) { int h ...

  7. node.js应用--转载

    最近,在向大学生们介绍 HTML5 的时候,我想要对他们进行问卷调查,并向他们显示实时更新的投票结果.鉴于此目的,我决定快速构建一个用于此目的的问卷调查应用程序.我想要一个简单的架构,不需要太多不同的 ...

  8. POJ 3415 Common Substrings(后缀数组)

    Description A substring of a string T is defined as: T(i, k)=TiTi+1...Ti+k-1, 1≤i≤i+k-1≤|T|. Given t ...

  9. POJ 2826 An Easy Problem?!(线段交点+简单计算)

    Description It's raining outside. Farmer Johnson's bull Ben wants some rain to water his flowers. Be ...

  10. 【转】Backbone.js学习笔记(一)

    文章转自: http://segmentfault.com/a/1190000002386651 基本概念 前言 昨天开始学Backbone.js,写篇笔记记录一下吧,一直对MVC模式挺好奇的,也对j ...