hdu5251 凸包 旋转卡壳

传送门:hdu5251 矩形面积

       接触了旋转卡壳不久,感觉很神奇……

       这个题是要寻找一个矩形来覆盖住桌面上的小矩形,小矩形即四个点啦,所以就是

找一个矩形覆盖住这些点,也就要求一个凸包,再用矩形把凸包盖住,那就很明显的

是旋转卡壳了,类似旋转卡壳求凸包的宽度,只是这是求矩形面积的最小值

完整代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN=1000005;
const double PI=acos(-1.0);
typedef long long ll;
struct point
{
    double x,y;
};
point list[MAXN];
point stack[MAXN];
int top;

/**叉积**/
double cross(point p0,point p1,point p2)
{
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
/**点积**/
double dot(point p0,point p1,point p2)
{
    return (p1.x-p0.x)*(p2.x-p0.x)+(p1.y-p0.y)*(p2.y-p0.y);
}
/**距离的平方**/
double dis(point p1,point p2)
{
    return (double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y);
}
/**极角排序**/
bool cmp(point p1,point p2)
{
    double tmp=cross(list[0],p1,p2);
    if(tmp>0) return true;
    else if(tmp==0&&dis(list[0],p1)<dis(list[0],p2)) return true;
    else return false;
}
/**凸包**/
void graham(int n)
{
    int i;
    if(n==1) {top=0;stack[0]=list[0];}
    if(n==2)
    {
        top=1;
        stack[0]=list[0];
        stack[1]=list[1];
    }
    if(n>2)
    {
        for(i=0;i<=1;i++) stack[i]=list[i];
        top=1;

        for(i=2;i<n;i++)
        {
            while(top>0&&cross(stack[top-1],stack[top],list[i])<=0) top--;
            top++;
            stack[top]=list[i];
        }
    }
}
/**旋转卡壳**/
double rotating_calipers(point p[],int n)
{
    int k1=1,k2=1,k3=1;
    double ans=0x7FFFFFFF;
    p[n]=p[0];
    for(int i=0;i<n;i++)
    {
        /**利用叉积的几何意义,来找距离p[i]-p[i+1]这条边的最远的点**/
        while(fabs(cross(p[i],p[i+1],p[k1]))<fabs(cross(p[i],p[i+1],p[k1+1])))
            k1=(k1+1)%n;
        /**这里求的点积,用了点积的几何意义,即p[i]-p[i+1]这条边再乘p[i]-p[k2]在
        p[i]-p[i+1]上的投影,投影的长度便是叉积再除p[i]-p[i+1]的长度,下面在求矩形面积
        时用到了这一点**/
        while(dot(p[i],p[i+1],p[k2])<dot(p[i],p[i+1],p[k2+1])||dot(p[i],p[i+1],p[k2])<0)
            k2=(k2+1)%n;///这里找的是最右边的点,根据几何意义,这里的点积应该是正的
        while(dot(p[i],p[i+1],p[k3])>dot(p[i],p[i+1],p[k3+1])||dot(p[i],p[i+1],p[k3])>0)
            k3=(k3+1)%n;///这里找的是最左边的点,根据几何意义,这里的点击应该是负的
        double tmp=fabs(cross(p[i],p[i+1],p[k1]));
        double tmp1=dot(p[i],p[i+1],p[k2]);
        double tmp2=dot(p[i],p[i+1],p[k3]);
        double d=dis(p[i],p[i+1]);
        /**这里求面积,实际是凸包的某一“宽度”(不是严格意义上
        的宽度)乘了两个叉积的差就是这个矩形了面积了**/
        double s=tmp/d*(tmp1-tmp2);
        ans=min(ans,s);
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    int cnt=0;
    while(t--)
    {
        cnt++;
        int n;
        scanf("%d",&n);
        int k=0;
        scanf("%lf%lf",&list[0].x,&list[0].y);
        for(int i=1;i<4*n;i++)
        {
            scanf("%lf%lf",&list[i].x,&list[i].y);
            if(list[i].y<list[k].y||(list[i].y==list[k].y&&list[i].x<list[k].x))
                k=i;
        }
        swap(list[k],list[0]);
        sort(list+1,list+4*n,cmp);

        graham(4*n);
        printf("Case #%d:\n%lld\n",cnt,(ll)(rotating_calipers(stack,top+1)+0.5));
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值