zoukankan      html  css  js  c++  java
  • 【洛谷3187】[HNOI2007] 最小矩形覆盖(旋转卡壳)

    点此看题面

    • 给定平面上(n)个点,求包含所有点的最小矩形面积及顶点坐标。
    • (nle5 imes10^4)

    旋转卡壳

    首先求出凸包,然后发现最小矩形肯定可以通过旋转满足与至少一条边相切,且另外三个方向上各至少有一个顶点。

    于是想到去枚举相切的这条边,则另外三个方向上的顶点就应该是到这条边距离最远的在这条边上的投影最小/最大(带符号值)的

    由于在枚举边的时候这三个点都单调移动,可以直接用双指针维护。

    然后要求矩形顶点坐标,先求出投影最小/最大的两个点在这条边上的投影,然后根据最远点到这条边的距离平移过去即可。

    代码:(O(nlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 50000
    #define DB double
    #define eps 1e-12
    using namespace std;
    int n;struct P
    {
    	DB x,y;I P(Cn DB& a=0,Cn DB& b=0):x(a),y(b){}
    	I P operator + (Cn P& o) Cn {return P(x+o.x,y+o.y);}
    	I P operator - (Cn P& o) Cn {return P(x-o.x,y-o.y);}
    	I P operator * (Cn DB& o) Cn {return P(x*o,y*o);}
    	I P operator / (Cn DB& o) Cn {return P(x/o,y/o);}
    	I DB operator * (Cn P& o) Cn {return x*o.x+y*o.y;}
    	I DB operator ^ (Cn P& o) Cn {return x*o.y-y*o.x;}
    	I bool operator < (Cn P& o) Cn {return x!=o.x?x<o.x:y<o.y;}
    	I bool operator == (Cn P& o) Cn {return fabs(x-o.x)<eps&&fabs(y-o.y)<eps;}
    	I DB L() Cn {return sqrt(x*x+y*y);}
    }p[N+5],s[N+5];
    I DB PtoS(Cn P& A,Cn P& X,Cn P& Y) {return ((Y-X)^(A-X))/(Y-X).L();}//求点到直线距离
    int m;I void Get()//求凸包
    {
    	#define pd(A,B,C) (fabs((C-B)^(B-A))<eps?(A<B)==(B<C):((C-B)^(B-A))>0)
    	RI i;sort(p+1,p+n+1),n=unique(p+1,p+n+1)-p-1;
    	for(i=1;i<=n;s[++m]=p[i++]) W(m>1&&pd(s[m-1],s[m],p[i])) --m;
    	for(i=n-1;i;s[++m]=p[i--]) W(m>1&&pd(s[m-1],s[m],p[i])) --m;--m;
    }
    I void Rotate()//旋转卡壳
    {
    	RI i,j=3,p=1,q=2;W((s[2]-s[1])*(s[p^1?p-1:m]-s[1])-(s[2]-s[1])*(s[p]-s[1])<eps) p=p^1?p-1:m;
    	DB ans=1e18;RI A,B,C,D;for(i=1;i<=m;++i)
    	{
    		W(((s[i+1]-s[i])^(s[j%m+1]-s[i]))-((s[i+1]-s[i])^(s[j]-s[i]))>-eps) j=j%m+1;//距离最远的
    		W((s[i+1]-s[i])*(s[p%m+1]-s[i])-(s[i+1]-s[i])*(s[p]-s[i])<eps) p=p%m+1;//投影最小的
    		W((s[i+1]-s[i])*(s[q%m+1]-s[i])-(s[i+1]-s[i])*(s[q]-s[i])>-eps) q=q%m+1;//投影最大的
    		if(ans<=PtoS(s[j],s[i],s[i+1])*((s[i+1]-s[i])*(s[q]-s[i])-(s[i+1]-s[i])*(s[p]-s[i]))/(s[i+1]-s[i]).L()) continue;//与已有答案比较
    		ans=PtoS(s[j],s[i],s[i+1])*((s[i+1]-s[i])*(s[q]-s[i])-(s[i+1]-s[i])*(s[p]-s[i]))/(s[i+1]-s[i]).L(),A=i,B=j,C=p,D=q;
    	}
    	P w[4],f=s[A+1]-s[A];f=f/f.L();P g(-f.y,f.x);
    	w[0]=s[A]+f*((s[A+1]-s[A])*(s[C]-s[A])/(s[A+1]-s[A]).L());//投影最小的点对应的投影点
    	w[1]=s[A]+f*((s[A+1]-s[A])*(s[D]-s[A])/(s[A+1]-s[A]).L());//投影最大的点对应的投影点
    	w[2]=w[1]+g*PtoS(s[B],s[A],s[A+1]),w[3]=w[0]+g*PtoS(s[B],s[A],s[A+1]);//平移到对面
    	RI x=0;for(printf("%.5lf
    ",ans),i=1;i^4;++i) (fabs(w[i].y-w[x].y)<eps?w[i].x<w[x].x:w[i].y<w[x].y)&&(x=i);
    	for(i=0;i^4;x=(x+1)%4,++i) printf("%.5lf %.5lf
    ",fabs(w[x].x)<eps?0:w[x].x,fabs(w[x].y)<eps?0:w[x].y);
    }
    int main()
    {
    	RI i;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%lf%lf",&p[i].x,&p[i].y);return Get(),Rotate(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    常用的adb命令
    Jmeter之计数器
    Jmeter跨线程组传递变量
    Jmeter的属性和变量
    Jmeter之关联——常用提取器
    Jmeter常用的逻辑控制器
    HDU 1262 寻找素数对 模拟题
    HDU 1431 素数回文 离线打表
    HDU 2553 N皇后问题
    HDU 2093 考试排名 模拟题
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3187.html
Copyright © 2011-2022 走看看