矩形包围起来的面积最小的矩形的面积(HDU5251)

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

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

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

一道很好想的计算几何题,但是实现起来很恶心,数学功力捉鸡,借用了强神的代码,稍加优化。

思路很简单,先求凸包,再旋转卡壳求面积,求矩形的高和宽很精髓,值得借鉴。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

const double EPS = 1e-3;
const double PI = acos(-1.0);
const int MAXV = 10005;

struct Point
{
    double x,y;
    double k;
    Point(){}
    Point(double _x,double _y)
    {
        x=_x;y=_y;
    }
    Point operator -(const Point &b)const
    {
        return Point(x-b.x,y-b.y);
    }
    double operator ^(const Point &b)const //叉乘
    {
        return x*b.y-y*b.x;
    }
    double operator *(const Point &b)const //点乘
    {
        return x*b.x+y*b.y;
    }
};

int res; //存储凸包个数
Point pre[4*MAXV], tu[4*MAXV];

int sgn(double x)
{
    if(fabs(x) < EPS) return 0;
    if(x < 0) return -1;
    else return 1;
}

double dis(Point a, Point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}

int cmp1(Point a, Point b) // 按坐标比较
{
    if(b.y != a.y)
        return a.y < b.y;
    return a.x < b.x;
}

int cmp2(Point a, Point b) //按角度比较
{
    if(a.k != b.k)
        return a.k < b.k;
    if(b.y != a.y)
        return a.y < b.y;
    return a.x < b.x;
}

double get_dis(Point a, Point b) // 获得a b方向的最远点距离b
{
    double ans = -(1<<29), temp;
    for(int i = 0; i < res; i++)
    {
        temp = ((b - a) * (tu[i] - b)) / dis(a,b);
        if(sgn(temp - ans) > 0) ans = temp;
    }
    return ans;
}

double get_h(Point a, Point b)//获得高
{
    double ans = -(1<<29),temp;
    for(int i = 0; i < res; i++)
    {
        temp = ((tu[i] - a) ^ (tu[i] - b))/dis(a,b);
        if(sgn(temp - ans) > 0) ans = temp;
    }
    return ans;
}

int main()
{
    int T, kcase = 1;
    scanf("%d", &T);
    while(T--)
    {
        int N;
        scanf("%d", &N);
        N*=4;
        for(int i = 0; i < N; i++) scanf("%lf%lf", &pre[i].x, &pre[i].y);
        sort(pre, pre + N, cmp1);
        for(int i = 1; i < N; i++) //求凸包
        {
            pre[i].k = atan2(pre[i].y - pre[0].y, pre[i].x - pre[0].x);
        }
        sort(pre + 1, pre + N, cmp2);
        //for(int i=1;i<N;i++) printf("%.3f\n",pre[i].k);
        tu[0] = pre[0];
        tu[1] = pre[1];
        res = 2;
        for(int i=2;i<N;i++)
        {
            while(res > 2 && ((tu[res-1]-tu[res-2])^(pre[i]-tu[res-1])) <= 0) res--;
            tu[res++] = pre[i];
            //printf("res=%d\n",res);
        }

        double ans = 1<<29;
        for(int i = 0; i < res; i++)//旋转卡壳
        {
            int j = (i + 1) % res;
            double dis1 = get_dis(tu[i], tu[j]);
            double dis2 = get_dis(tu[j], tu[i]);
            double d = dis(tu[i], tu[j]) + dis1 + dis2;
            double h = get_h(tu[i], tu[j]);
            double temp = d * h;
            if(sgn(temp - ans) < 0) ans = temp;
        }
        printf("Case #%d:\n",kcase++);
        printf("%.0lf\n",ans + EPS);
        //printf("res = %d\n",res);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值