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);
}
}