BZOJ1185[HNOI2007] 最小矩形覆盖(旋转卡壳)
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] 最小矩形覆盖(旋转卡壳)的更多相关文章
- bzoj1185 [HNOI2007]最小矩形覆盖 旋转卡壳求凸包
		[HNOI2007]最小矩形覆盖 Time Limit: 10 Sec Memory Limit: 162 MBSec Special JudgeSubmit: 2081 Solved: 920 ... 
- BZOJ 1185: [HNOI2007]最小矩形覆盖 [旋转卡壳]
		1185: [HNOI2007]最小矩形覆盖 Time Limit: 10 Sec Memory Limit: 162 MBSec Special JudgeSubmit: 1435 Solve ... 
- 洛谷 P3187 BZOJ 1185 [HNOI2007]最小矩形覆盖 (旋转卡壳)
		题目链接: 洛谷 P3187 [HNOI2007]最小矩形覆盖 BZOJ 1185: [HNOI2007]最小矩形覆盖 Description 给定一些点的坐标,要求求能够覆盖所有点的最小面积的矩形, ... 
- 【bzoj1185】[HNOI2007]最小矩形覆盖 (旋转卡壳)
		给你一些点,让你用最小的矩形覆盖这些点 首先有一个结论,矩形的一条边一定在凸包上!!! 枚举凸包上的边 用旋转卡壳在凸包上找矩形另外三点... 注意精度问题 #include<cstdio> ... 
- bzoj 1185 [HNOI2007]最小矩形覆盖——旋转卡壳
		题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1185 矩形一定贴着凸包的一条边.不过只是感觉这样. 枚举一条边,对面的点就是正常的旋转卡壳. ... 
- BZOJ 1185: [HNOI2007]最小矩形覆盖-旋转卡壳法求点集最小外接矩形(面积)并输出四个顶点坐标-备忘板子
		来源:旋转卡壳法求点集最小外接矩形(面积)并输出四个顶点坐标 BZOJ又崩了,直接贴一下人家的代码. 代码: #include"stdio.h" #include"str ... 
- BZOJ1185 [HNOI2007]最小矩形覆盖  【旋转卡壳】
		题目链接 BZOJ1185 题解 最小矩形一定有一条边在凸包上,枚举这条边,然后旋转卡壳维护另外三个端点即可 计算几何细节极多 维护另外三个端点尽量不在这条边上,意味着左端点尽量靠后,右端点尽量靠前, ... 
- 2018.10.18 bzoj1185: [HNOI2007]最小矩形覆盖(旋转卡壳)
		传送门 不难看出最后的矩形一定有一条边与凸包某条边重合. 因此先求出凸包,然后旋转卡壳求出当前最小矩形面积更新答案. 代码: #include<bits/stdc++.h> #define ... 
- BZOJ1185 HNOI2007 最小矩形覆盖 凸包、旋转卡壳
		传送门 首先,肯定只有凸包上的点会限制这个矩形,所以建立凸包. 然后可以知道,矩形上一定有一条边与凸包上的边重合,否则可以转一下使得它重合,答案会更小. 于是沿着凸包枚举这一条边,通过旋转卡壳找到离这 ... 
随机推荐
- HTML/CSS实现文字环绕图片布局
			原文: https://blog.csdn.net/yiyelanxin/article/details/75006925 在一个图文并茂的网页上,文字环绕图片可以使布局美观紧凑,如何实现呢?有两种办 ... 
- JetSonNano darknet yolov3工程通过CMakeLists.txt配置编译环境
			CMakeLists.txt 写的比较糙,有疑问欢迎咨询. option(GPU ON) option(CUDNN ON) option(OPENCV ON) cmake_minimum_requir ... 
- CSS盒模型面试知识点
			一.基本概念 1.基本概念:标准盒模型+怪异盒模型(IE模型) 基本组成:由margin.padding.content组成. 2.标准盒模型和IE模型的区别 标准盒模型中width指的是内容区域co ... 
- react native之封装离线缓存框架
			请求数据=>本地有无缓存+缓存数据是否过期 =>可用 =>不可用 将代码封装成一个DataStore.js文件, 这里面主要提供:从本地获取数据,从网络获取数据,创建本地时间戳,请求 ... 
- Java使用google身份验证器实现动态口令验证
			参考: 1)https://www.jb51.net/article/121243.htm 2)https://www.cnblogs.com/wuaili/p/9810661.html 
- PHP入门培训教程PHP程序员要掌握哪些技术
			总有那么一群人,学个半吊子就急着找工作,面试题做不出来,吹牛都吹不来所以你只能低工资.PHP程序员要掌握哪些技术?那么兄弟连PHP培训 就来小结一下. 面试前请参考:(前三阶段完成80%在北京月薪5k ... 
- R 动态定义变量名 assign
			rm(list=ls()) library(GSVA) library(GSEABase) library(GSVAdata) library(msigdbr) library(org.Hs.eg.d ... 
- 使用visual studio配置和运行《opengl圣经》的第一个案例
			对vc++新手来说,想把opengl圣经里的教学案例运行起来还真不是一件容易的事情,而且并没有完整的操作流程教学,这里就总结一下吧: 先安装cmake,用于生成vs的工程文件,安装过程中选中“将目录添 ... 
- SQL报错:ORA-00911:无效的字符错误
			转载自:https://blog.csdn.net/huangyanlong/article/details/38096469 *)ORA-00911:无效的字符错误——由编译环境下一个小错误引起.S ... 
- Splinter 的认识和基础应用
			Splinter 是一个使用Python开发的开源web应用测试程序,它可以帮助我们实现自动浏览站点和与其进行交互.它是依赖于其它python插件或拓展进行的,所以我们使用它之前需要安装一系列的依赖包 ... 
