矩形面积 HDU - 5251 (旋转卡壳 求最小矩形覆盖)

本文介绍了一种使用凸包算法解决特定矩形面积计算问题的方法。通过枚举矩形的一边作为凸包上的边,然后确定矩形的其他三个顶点,利用叉积和点积计算最大距离和右侧顶点,最终计算出矩形的面积。文章详细解释了如何通过旋转卡壳优化时间复杂度,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5251
开始没想到用凸包,看了大佬的博客才明白。
思路:矩形各边必经过凸包上的点,并且矩形一边为凸包上的边,另外三边经过凸包上的点。先枚举矩形的一个边(即凸包上的边),再确定另外三点。矩形为高低左右四边,假设此边为为低边(相对意义上的),再确定另外三边上的三个点即可。与此边距离最大的点是高边上的点,利用叉积来求,因为三角形底确定,高就是距离,面积越大,距离越大;在此边最右边的点为 右边上的点,利用点积来求,因为点积反映了b向量在a向量方向上的投影,点积越大,越右左边上的点也用点积来求。确定了边和三点,之后算面积。
和往常一样,利用旋转卡壳是凸函数的性质来优化时间
确定三点时,判别式用大于等于或小于等于而非大于或小于。

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
const int MAXN=4005;
const double PI=acos(-1.0);
int flag;
struct point
{
    int x,y;
};
point list[MAXN];
int stack[MAXN],top;

int cross(point p0,point p1,point p2) //计算叉积  p0p1 X p0p2
{
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
int dotproduct(point p0,point p1,point p2) //计算点积  p0p1 * p0p2
{
    int x1=p1.x-p0.x;
    int x2=p2.x-p0.x;
    int y1=p1.y-p0.y;
    int y2=p2.y-p0.y;
    return x1*x2+y1*y2;
}
double dis(point p1,point p2)  //计算 p1p2的 距离
{
    return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
double dis1(point p1,point p2)  //计算 p1p2的 距离平方
{
    return ((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(point p1,point p2) //极角排序函数 , 角度相同则距离小的在前面
{
    int 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 init(int n) //输入,并把  最左下方的点放在 list[0]  。并且进行极角排序
{
    int i,k;
    point p0;
    p0.x=list[0].x;
    p0.y=list[0].y;
    k=0;
    for(i=1;i<n;i++)
    {
        if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)) )
        {
            p0.x=list[i].x;
            p0.y=list[i].y;
            k=i;
        }
    }
    list[k]=list[0];
    list[0]=p0;
    sort(list+1,list+n,cmp);
    //printf("&&\n");
}

void graham(int n)
{
    int i;
    if(n==1) {top=0;stack[0]=0;}
    if(n==2)
    {
        top=1;
        stack[0]=0;
        stack[1]=1;
    }
    if(n>2)
    {
        for(i=0;i<=1;i++) stack[i]=i;
        top=1;

        for(i=2;i<n;i++)
        {
            while(top>0&&cross(list[stack[top-1]],list[stack[top]],list[i])<=0) top--;
            top++;
            stack[top]=i;
        }
        stack[++top]=0;
    }
    /*for(int i=0;i<=top;i++)
    {
        printf("%d %d*",list[stack[i]].x,list[stack[i]].y);
    }
    printf("&&\n");*/
}

int main()
{
     int t,l,r,h,n,x;
     double sum;
     scanf("%d",&t);
     x=1;
     while(t--)
     {
         scanf("%d",&n);
         n=n*4;
         for(int i=0;i<n;i++)
         {
             scanf("%d%d",&list[i].x,&list[i].y);
         }
         init(n);
         graham(n);
         l=r=h=1;
         for(int i=0;i<top;i++)
         {
             while(cross(list[stack[i]],list[stack[i+1]],list[stack[h+1]])>=
            cross(list[stack[i]],list[stack[i+1]],list[stack[h]]))/// 叉积  不要将等号去掉  沙雕了
                h=(h+1)%top;
  while(dotproduct(list[stack[i]],list[stack[i+1]],list[stack[r+1]])>=
    dotproduct(list[stack[i]],list[stack[i+1]],list[stack[r]]))///点积
                r=(r+1)%top;
                if(i==0)  l=r;
while(dotproduct(list[stack[i]],list[stack[i+1]],list[stack[l+1]])<=
    dotproduct(list[stack[i]],list[stack[i+1]],list[stack[l]]))///点积
                l=(l+1)%top;
                double d=dis1(list[stack[i]],list[stack[i+1]]);
                if(i==0)
                {
                    sum=(double)fabs(cross(list[stack[i]],list[stack[i+1]],list[stack[h]]))*
fabs(dotproduct(list[stack[i]],list[stack[i+1]],list[stack[r]])-dotproduct(list[stack[i]],list[stack[i+1]],list[stack[l]]))/d;
                }
                else
                {
double sum1=(double)(cross(list[stack[i]],list[stack[i+1]],list[stack[h]]))*
fabs(dotproduct(list[stack[i]],list[stack[i+1]],list[stack[r]])-dotproduct(list[stack[i]],list[stack[i+1]],list[stack[l]]))/d;
                    if(sum1<sum)
                        sum=sum1;
                }
         }
         long long ans=sum+0.5;
         printf("Case #%d:\n",x);
         printf("%lld\n",ans);
         x++;
     }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值