题目链接:
POJ 3301 Texas Trip
题意:
平面给
n
个整数点,求覆盖这
数据范围:
n≤30,每个点距离原点的距离不超过500
分析:
先考虑如果水平竖直地放置正方形(边和坐标轴平行)圈住所有点的最小正方形的边长是:
L=max(xmax−xmin,ymax−ymin)
根据经验(?):如果旋转正方形,可以发现覆盖这 n 个点的最小正方形面积是先减后增的。符合凸性函数的性质,所以我们可以枚举旋转的角度。
但是枚举角度计算正方形的话比较麻烦,可以选择旋转平面上的点,使得正方形仍然是水平竖直放置的,因为这样计算正方形的边长比较方便。
如果把点表示成极坐标形式:
那么顺时针旋转 α 角度后:
x′=r×cos(θ−α),y=r×sin(θ−α)
化简一下可得:
x′=r×cosθ×cosα+r×sinθ×sinα=x×cosα+y×sinα
y′=r×sinθ×cosα−r×cosθ×sinα=y×cosα−x×sinα
这样子我们就得到了旋转后的坐标,计算此时的最小正方形,三分角度,比较 mid 和 midmid 即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const int MAX_N = 50;
const double eps = 1e-12;
const double pi = acos(-1.0);
int n, T;
double x[MAX_N], y[MAX_N];
double rotate(double alp)
{
double Minx = 1e5, Miny = 1e5, Maxx = -1.0, Maxy = -1.0, tmpx, tmpy;
for(int i = 0; i < n; ++i) {
tmpx = x[i] * cos(alp) + y[i] * sin(alp);
tmpy = y[i] * cos(alp) - x[i] * sin(alp);
Minx = min(Minx, tmpx), Miny = min(Miny, tmpy);
Maxx = max(Maxx, tmpx), Maxy = max(Maxy, tmpy);
}
double len = max(Maxx - Minx, Maxy - Miny);
return len * len;
}
int main()
{
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 0; i < n; ++i) {
scanf ("%lf%lf", x + i, y + i);
}
double low = 0.0, high = pi / 2.0, mid, midmid, res1, res2;
for(int i = 0; i < 100; ++i) {
mid = (low + high) / 2.0;
midmid = (high + mid) / 2.0;
res1 = rotate(mid);
res2 = rotate(midmid);
if(res1 < res2) high = midmid;
else low = mid;
}
printf("%.2lf\n", rotate(mid));
}
return 0;
}