首页 > 其他分享 >[题解]P3187 [HNOI2007] 最小矩形覆盖

[题解]P3187 [HNOI2007] 最小矩形覆盖

时间:2024-07-24 16:17:34浏览次数:7  
标签:P3187 le 边界 题解 top st vec HNOI2007 矩形

P3187 [HNOI2007] 最小矩形覆盖

调了半天居然是因为没判断浮点精度误差才\(\colorbox{IndianRed}{\texttt{\color{White}{WA}}}\)了\(3\)个点,其他都没有问题!警钟长鸣……


首先有一个结论:凸多边形的最小外接矩形一定和它的一条边重合。

这个结论,网上几乎找不到完整的证明,目前发现较为清晰的证明是2008年的一篇 一个求解多边形最小面积外接矩形的算法论文(第\(3\)页)。然而这个证明不完整,没有考虑\(4\)个交点的情况中,\(\angle\gamma\)和\(\angle\alpha\)不在对角线划分区域的同一个,如下图:

这种情况仍然能用\(\alpha\)和\(\gamma\)表示出矩形面积,但在这个表达式的取值范围内,面积并不是单调的……总之如果能证出这种情况,整个命题就得证了。大家如果有想法可以发在评论区。

根据这个结论,我们就可以枚举那条和矩形边重合的边\(a\)。进而找到离这条边最远的点,作为矩形的上边界;在垂直于边\(a\)方向上,找左右最远的两点,作为矩形的左、右边界即可。

找左右边界如果和边\(a\)进行比较的话,可以用点积:

\[\vec{a}\cdot\vec{b}=|a||b|\cos{\angle(\vec{a},\vec{b})} \]

因此\(\vec{a},\vec{b}\)同向,即\(|\angle(\vec{a},\vec{b})|<\frac{\pi}{2}\)的话,点积\(>0\)。反向则\(<0\),垂直则\(=0\)。

用叉积的话也可以,不过不能和\(a\)做,而是和与\(a\)垂直的向量做(代码用的这种方法)。

与旋转卡壳类似地,随着枚举的边\(a\)的旋转,其他边界顶点仅需从上一次的位置开始移动即可。


找到边界后我们还需要求出四边形的底,高(底就是和边\(a\)重合的那一条边)。高就是\(S\div a\),其中\(S\)是\(\vec{a}\)与上边界形成的平行四边形面积。底的求法详细说一下:

为什么投影长度能转换成点乘呢?因为\(\vec{u}\cdot\vec{a}\)的定义就是\(\vec{u}\)在\(\vec{a}\)上投影的长度再乘\(|\vec{a}|\)嘛,所以再除掉一个\(|\vec{a}|\)就是投影长度了。

为什么要减去呢?因为\(\vec{v}\)的投影长度相对\(\vec{a}\)来说是负数,因此需要变成减号。

点乘公式:\(\vec{a}\cdot\vec{b}=x_a x_b+y_a y_b\)。

注:代码实现中求的是顺时针的凸包,所以\(\vec{a}\)是顺时针的,和图中不一样,自然长度的左减右需要变成右减左。

得到长度之后,四个顶点坐标自然就好求了,具体见代码。


注意精度问题!

点击查看代码
#include<bits/stdc++.h>
#define nxtnode(x) ((x%top)+1)
#define eps 1e-6
#define N 50010
using namespace std;
struct vec{
	double x,y;
	vec operator+(const vec b){return {x+b.x,y+b.y};}
	vec operator-(const vec b){return {x-b.x,y-b.y};}
	vec operator*(const double b){return {b*x,b*y};}
	double len(){return sqrt(x*x+y*y);}
}a[N],minans[4];//左下,右下,右上,左上
bool cmp(vec a,vec b){return a.x==b.x?a.y<b.y:a.x<b.x;}
inline double cross(vec a,vec b){return a.x*b.y-a.y*b.x;}
inline double dot(vec a,vec b){return a.x*b.x+a.y*b.y;}
int n,st[N],top;
double minn=1e8;
void convex_hull(){//顺时针凸包
	sort(a+1,a+1+n,cmp);
	st[top=1]=1;
	for(int i=2;i<=n;i++){
		while(top>1&&cross(a[st[top]]-a[st[top-1]],a[i]-a[st[top]])>-eps)
			top--;
		st[++top]=i;
	}
	int tmp=top;
	for(int i=n-1;i>=1;i--){
		while(top!=tmp&&cross(a[st[top]]-a[st[top-1]],a[i]-a[st[top]])>-eps)
			top--;
		st[++top]=i;
	}
	top--;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;
	convex_hull();
	int to=1,le=1,ri;
	//to上边界,le左边界,ri右边界
	for(int i=1;i<=top;i++){
		vec bo=a[st[nxtnode(i)]]-a[st[i]];//枚举重合的边
		double len=bo.len();
		vec vert={bo.y/len,-bo.x/len};//垂直于bo,长度为1的向量
		while(cross(bo,a[st[to]]-a[st[i]])-cross(bo,a[st[nxtnode(to)]]-a[st[i]])>-eps)
			to=nxtnode(to);//找上边界
		if(i==1) ri=to;//注意必须赋初值为to,否则ri就卡在第1个点不动了
		while(cross(a[st[le]]-a[st[i]],vert)-cross(a[st[nxtnode(le)]]-a[st[i]],vert)>-eps)
			le=nxtnode(le);//找左边界
		while(cross(a[st[ri]]-a[st[i]],vert)-cross(a[st[nxtnode(ri)]]-a[st[i]],vert)<eps)
			ri=nxtnode(ri);//找右边界
		double lb=dot(a[st[le]]-a[st[i]],bo)/len,rb=dot(a[st[ri]]-a[st[i]],bo)/len;//用点积计算投影长度
		double W=cross(a[st[to]]-a[st[i]],bo)/len,L=lb-rb;//高,底
		if(W*L<minn){
			minn=W*L;
			minans[0]=bo*(lb/len)+a[st[i]],minans[1]=bo*(rb/len)+a[st[i]];
			minans[2]=minans[1]+vert*W,minans[3]=minans[0]+vert*W;
		}
	}
	cout.setf(ios_base::fixed);
	cout.precision(5);
	cout<<minn<<"\n";
	for(int i=0;i<4;i++)
		cout<<minans[i].x<<" "<<minans[i].y<<"\n";
	return 0;
}

标签:P3187,le,边界,题解,top,st,vec,HNOI2007,矩形
From: https://www.cnblogs.com/Sinktank/p/18315132

相关文章

  • [POI2012] PRE-Prefixuffix 题解
    前言题目链接:洛谷。题意简述给出长为\(n\)的串\(\texttt{S}\)。求最大的\(l\)满足:\[2l\leqn\land\texttt{S}[1\ldotsl]\doteq\texttt{S}[n-l+1\ldotsn]\]其中\(\doteq\)表示循环相同。题目分析看到循环相同,套路化想到,两个字符串一定可以表示成\(\tex......
  • 题解:2024牛客多校第三场 B
    BCrashTestheader时间限制:C/C++2秒,其他语言4秒空间限制:C/C++1048576K,其他语言2097152K64bitIOFormat:%lld题目描述Afterfiveyears,themosthigh-profileeventinmotorracing,Formula1,returnstoChina.TheChineseGrandPrixwasrecentlyheldatthe......
  • 题解:P10450 [USACO03MAR] Best Cow Fences G
    题目链接O(n^3)做法直接暴力枚举长度、起点,再全部跑一边求平均数。附上我丑陋的代码和提交记录,这个代码可以得42分。#include<bits/stdc++.h>usingnamespacestd;constintNR=1e5+5;longlongn,l,a[NR],sum,ave;intmain(){ cin>>n>>l; for(inti......
  • [CEOI2011] Matching 题解
    前言题目链接:洛谷。在上一题之后,模拟赛又放了一道KMP重定义相等的问题,但是寄了,故再记之。题意简述现在给出\(1\simn\)的排列\(p\)和序列\(h_1,h_2,\cdots,h_m\)​​,请你求出哪些\(h\)的子串符合排列\(p\)。串\(a_i\)符合一个排列被定义为其从小到大排序后得......
  • ABC250H 题解
    题面我们先考虑如何让连续的不在房子中的时间尽量短:我们考虑两个有房子的点\(x,y\),如果\(x\rightsquigarrowu\xrightarrow{w}v\rightsquigarrowy\)这条路径上除了\(x,y\)不存在有房子的点,那么我们可以找到这样一条路径,一定不劣:令\(a,b\)分别为最靠近\(u,v\)的有房......
  • CF547D Mike and Fish 题解
    Description给定\(n\)个整点。你要给每个点染成红色或蓝色。要求同一水平线或垂直线上两种颜色的数量最多相差\(1\)。\(n,x_i,y_i\le2\times10^5\)。Solution考虑给每条水平线和垂直线建一个点,对于每个整点就把它对应的两条线连一条边。题目就转化为了给每条边定......
  • ARC117F Gateau 题解
    ARC117FGateau题解题解区好像都没有对dp详细解释,本文将稍细致地说一说dp部分。题目大意给定一个长度为\(2N\)的环,环上每个节点有属性值\(B_i\(i=0,\dots,2N-1)\)和\(2N\)个限制,第\(i\)个限制形如\(\sum\limits_{j=i}^{i+N-1}B_j\geqA_i\),向环上的节点赋值,使得......
  • 【保奖思路】2024年华为杯研究生数学建模D题解题思路分享(点个关注,后续会更新
    您的点赞收藏是我继续更新的最大动力!一定要点击如下的卡片链接,那是获取资料的入口!点击链接加入群聊【2024华为杯研赛资料汇】:http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=MwNruKbEvR9aZns1l7xXBWmQQKYAEh-F&authKey=igaBN79pz6BhNlDQ6TIZ5YFAbn71aDcYL77uANPquLF%2FTVgeSAhEZ......
  • 题解|2024暑期牛客多校03
    【原文链接】比赛链接:2024牛客暑期多校训练营3A.BridgingtheGap2题目大意nnn个人过河,第i......
  • P10280 [USACO24OPEN] Cowreography G 题解
    Description奶牛们组了一支舞蹈队,FarmerJohn是她们的编舞!舞蹈队最新而最精彩的舞蹈有\(N\)头奶牛(\(2\leN\le10^6\))排成一行。舞蹈中的每次动作都涉及两头奶牛,至多相距\(K\)个位置(\(1\leK<N\)),优雅地跳起并降落在对方的位置上。队伍中有两种奶牛——更赛牛(Guernsey)和荷......