HDU 5251 矩形面积 (计算几何+旋转卡壳求覆盖凸包的最小矩形面积)

小度熊有一个桌面,小度熊剪了很多矩形放在桌面上,小度熊想知道能把这些矩形包围起来的面积最小的矩形的面积是多少。

Input
第一行一个正整数 T,代表测试数据组数( 1T20 ),接下来 T 组测试数据。

每组测试数据占若干行,第一行一个正整数 N(1N<1000) ,代表矩形的数量。接下来 N 行,每行 8 个整数 x1,y1,x2,y2,x3,y3,x4,y4
,代表矩形的四个点坐标,坐标绝对值不会超过10000。
Output
对于每组测试数据,输出两行:

第一行输出"Case #i:",i 代表第 i 组测试数据。
第二行包含1 个数字,代表面积最小的矩形的面积,结果保留到整数位。
Sample Input
2
2
5 10 5 8 3 10 3 8
8 8 8 6 7 8 7 6
1
0 0 2 2 2 0 0 2
Sample Output
Case #1:
17
Case #2:
4

这是好久之前写的题然后好久没碰旋转卡壳就都忘了QAQ。。。。

这个题先求这N个小矩形的凸包再用旋转卡壳求出覆盖这个凸包的最小矩形面积

roll() 函数即是旋转卡壳了

对于找覆盖凸包的最小矩形可以这样写:

这个矩形必有至少一条边与凸包的某一条边重合,

在凸包上遍历这条边,根据这条边在凸包上找到与之对应的最上面的点 p1,和最左点和最右点 p2,p3,这些点都是凸包的顶点

然后矩形的长宽分别是 p1 到这条边的距离以及过 p2,p3 所做的与这条边垂直的一对平行线之间的距离

根据长宽求面积取最小值

emmmmm..................................



代码:

#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cstdio>
#include <set>
#include <cmath>
#include <map>
#include <algorithm>
#define INF 0x3f3f3f3f
#define MAXN 10000005
#define Mod 10001
using namespace std;
typedef long long LL;
#define point pair<double,double>
#define x first
#define y second
double dot(point o,point a,point b)
{
    return (a.x-o.x)*(b.x-o.x)+(a.y-o.y)*(b.y-o.y);
}
double cross(point o,point a,point b)
{
    return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
}
double dist(point a,point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double dist(point o,point a,point b)
{
    return fabs(cross(o,a,b))/dist(a,b);
}
double dist(point o1,point o2,point a,point b)
{
    double l=dist(a,b);
    return l+fabs(dot(b,a,o1))/l+fabs(dot(a,b,o2))/l;
}

point p[4000+233];

int n,s[4000+233],top;

bool cmp(point p1,point p2) //极角排序函数 , 角度相同则距离小的在前面
{
    int tmp=cross(p[0],p1,p2);
    if(tmp>0) return true;
    else if(tmp==0&&dist(p[0],p1)<dist(p[0],p2)) return true;
    else return false;
}
void init() //输入,并把  最左下方的点放在 list[0]  。并且进行极角排序
{
    int i,k;
    point p0;
    scanf("%lf%lf",&p[0].x,&p[0].y);
    p0.x=p[0].x;
    p0.y=p[0].y;
    k=0;
    for(i=1;i<n;i++)
    {
        scanf("%lf%lf",&p[i].x,&p[i].y);
        if( (p0.y>p[i].y) || ((p0.y==p[i].y)&&(p0.x>p[i].x)) )
        {
            p0.x=p[i].x;
            p0.y=p[i].y;
            k=i;
        }
    }
    p[k]=p[0];
    p[0]=p0;

    sort(p+1,p+n,cmp);
}

void graham()
{
    int i;
    if(n==1) {top=0;s[0]=0;}
    if(n==2)
    {
        top=1;
        s[0]=0;
        s[1]=1;
    }
    if(n>2)
    {
        for(i=0;i<=1;i++) s[i]=i;
        top=1;
        for(i=2;i<n;i++)
        {
            while(top>0&&cross(p[s[top-1]],p[s[top]],p[i])<=0) top--;
            top++;
            s[top]=i;
        }
    }
}
double ans=1e16;
point a[4000+233];
void roll()
{
    ans=1e16;
    for(int i=0;i<=top;i++)
    {
        a[i]=p[s[i]];
    }
    top++;
    a[top]=p[s[0]];
    int pos1=0,pos2=1;//pos1,pos2表示在凸包上遍历那条边,根据这条边找可以覆盖凸包的矩形
    int p1=1,p2=1,p3=1;//分别是相对于边 <pos1,pos2> 最上最左最右的点(p2,p3哪个左哪个右我忘了)
    for(;pos1<top;)
    {
        while(fabs(cross(a[pos2],a[pos1],a[p1]))<=fabs(cross(a[pos2],a[pos1],a[(p1+1)%top])))
        {
           p1++;
           p1%=top;
        }
        while(dot(a[pos2],a[pos1],a[p2])>=dot(a[pos2],a[pos1],a[(p2+1)%top]))
        {
            p2++;
            p2%=top;
        }
        if(pos1==0)p3=p2;
        while(dot(a[pos2],a[pos1],a[p3])<=dot(a[pos2],a[pos1],a[(p3+1)%top]))
        {
            p3++;
            p3%=top;
        }
        double h=dist(a[p1],a[pos1],a[pos2]);
        double l=dist(a[p2],a[p3],a[pos1],a[pos2]);
//        printf("\npos1: %.0f %.0f\n",a[pos1].x,a[pos1].y);
//        printf("pos2: %.0f %.0f\n",a[pos2].x,a[pos2].y);
//        printf("p1:   %.0f %.0f\n",a[p1].x,a[p1].y);
//        printf("p2:   %.0f %.0f\n",a[p2].x,a[p2].y);
//        printf("p3:   %.0f %.0f\n",a[p3].x,a[p3].y);
        ans=min(ans,h*l);
        pos1++;
        pos2++;
    }
}
int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        n*=4;
        init();
        graham();
//        for(int i=0;i<=top;i++)
//        {
//            printf("%.0f %.0f\n",p[s[i]].x,p[s[i]].y);
//        }
        roll();
        printf("Case #%d:\n%.0f\n",++cas,ans);
    }
    return 0;
}
本人蒟蒻,如有错误,还望指正

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值