• 【洛谷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;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    从txt读取数据到Cvmat
    PCA之后进行归一化
    vc中调用exe文件并传递参数
    C#安装包过程
    电脑技巧
    DLL文件的创建与调用
    SVM调用方法
    舌顶上腭位置与作用
    KNN算法的實現
    How to Read a Paper
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3187.html
Copyright © 2020-2023  润新知