【BZOJ1185】最小矩形覆盖 计算几何 凸包 旋转卡壳

写完程序两小时,调对精度一小时hhh

首先不严格的直觉告诉我们所求矩形一定有一条边在凸包上,我们遍历凸包上的边,用类似旋转卡壳的方式得到凸包上的三个点,分别是在边上投影最靠前的点,在边上投影最靠后的点,距离边最远的点 ,这三个点一定在所求矩形的另外三条边上。得到一个矩形求解即可。


我尽力了。。。

判断1号点和2号点时要同时运用内积(点乘)和外积(叉乘)与0的大小关系,具体见代码

/**************************************************************
    Problem: 1185
    User: RicardoWang
    Language: C++
    Result: Accepted
    Time:236 ms
    Memory:2840 kb
****************************************************************/
 
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define maxn 50005
double eps=1e-8;
double fabs(double x)
{
    return x<0 ?-x:x;
}
struct P
{
    double x,y;
    P(double x=0,double y=0) : x(x),y(y) {}
}A[maxn],s[maxn];
double operator * (P x,P y)
{
    return x.x*y.y-x.y*y.x;
}
P operator - (P x,P y)
{
    return P(x.x-y.x,x.y-y.y);
}
double dis(P x,P y)
{
    return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}
bool operator < (P x,P y)
{
    double t=(x-A[1])*(y-A[1]);
    if(t==0)return dis(x,A[1])<dis(y,A[1]);
    else return t>0;
}
int n,top;
void Init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf",&A[i].x,&A[i].y);
    }
    return ;
}
void graham()
{
    int k=1;
    for(int i=1;i<=n;i++)if(A[i].x<A[k].x ||(A[i].x == A[k].x && A[i].y<A[k].y))k=i;
    if(k!=1)swap(A[k],A[1]);
    sort(A+2,A+1+n);
    s[++top]=A[1];
    for(int i=2;i<=n;i++)
    {
        while(top>1 && (s[top]-s[top-1])*(A[i]-s[top])<=eps)top--;
        s[++top]=A[i];
    }
    for(int i=1;i<=top;i++)
    {
//      printf("%.5lf %.5lf\n",s[i].x,s[i].y);
    }
    return ;
      
}
struct line
{
    double A,B,C;
    void get(P x,P y)
    {
        if(fabs(x.x-y.x)<=eps)
        {
            A=1; B=0; C=-x.x;
        }
        else
        {
            A=(y.y-x.y)/(y.x-x.x); B=-1; C=-A*x.x-B*x.y;
        }
        return ;
    }
    void getdx(P x,P y)
    {
        A=y.x; B=y.y; C=-A*x.x-B*x.y;
    }
}S[5];
P ans[5],t[5];
P operator * (line x,line y)
{
    P ans;
    double f,X,Y;
    if(fabs(y.B)<=eps)
    {
        X=(-y.C/y.A); Y=(x.A*X+x.C)/(-x.B);
    }
    else
    {
        f=-x.B/y.B; X=-(x.C+f*y.C)/(x.A+f*y.A); Y=(y.A*X+y.C)/(-y.B);
    }
    return P (X,Y);
}
double Sum=-1;
void getS(int A,int B,int x,int y,int z)
{
    P vec=s[B]-s[A];
    S[1].get(s[A],s[B]);
    S[2].getdx(s[x],vec);
    S[3].getdx(s[y],P(vec.y,-vec.x));
    S[4].getdx(s[z],vec);
      
    P c[5];
    for(int i=1;i<=4;i++)c[i]=S[i]*S[i%4+1];
    double ss=(c[2]-c[1])*(c[4]-c[1]);if(ss<0)ss=-ss;
    if(ss<Sum || Sum==-1)
    {
        Sum=ss;
        for(int i=1;i<=4;i++)ans[i]=c[i];
    }
      
    return ;
}
double dian(P x,P y)
{
    return x.x*y.x+x.y*y.y;
}
 
void work()
{
    int max1=2,max2=2,max3=2,j;
    for(int i=1;i<=top;i++)
    {
        j=i%top+1;
        while (max1%top+1!=i && !((s[max1%top+1]-s[max1])*(s[j]-s[i])<=0 && dian((s[max1%top+1]-s[max1]),s[j]-s[i])<=0 ))max1=max1%top+1;
        while (max3%top+1!=i && !((s[max3%top+1]-s[max3])*(s[j]-s[i])>=0 && dian((s[max3%top+1]-s[max3]),s[j]-s[i])>=0 ))max3=max3%top+1;
        while (max2%top+1!=i && (s[j]-s[i])*(s[max2%top+1]-s[max2])>=0 )max2=max2%top+1;
        getS(i,j,max1,max2,max3);
    }
    int first=1;
    for(int i=2;i<=4;i++)if((fabs(ans[i].y-ans[first].y)>eps && ans[i].y<ans[first].y) || (fabs(ans[i].y-ans[first].y)<=eps && ans[i].x<ans[first].x))first=i;
    printf("%.5lf\n",Sum);
    for(int i=1;i<=4;i++)
    {
        if(fabs(ans[i].x)<=eps)ans[i].x=0;
        if(fabs(ans[i].y)<=eps)ans[i].y=0;
    }
    for(int i=1;i<=4;i++)
    {printf("%.5lf %.5lf\n",ans[first].x,ans[first].y);first=first%4+1;}
    return ;
}
int main()
{
   // freopen("in.txt","r",stdin);
    Init();
    graham();
  
    work();
      
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值