题目链接:http://poj.org/problem?id=3301
题目大意
给出平面中的点集,求可以覆盖这些点的最小面积正方形。
题目分析
问题是要求最小的正方形,假设这个正方形的边都是分别与坐标轴平行,也就是说正方形没有旋转一定的角度,那么我们只要考虑最上,最下,最左,最右 的点即可,当正方形旋转过一定的角度d是我们也只要考虑最边上的点的距离差即可(故这题也可用枚举旋转角度的方法来求解,但要注意步长的选取以保证精度)。
假设旋转角度为d,那么枚举每两个点关于旋转角度为d的直线距离取最大值,即可保证覆盖所有的点,在两个方向上这样的距离分别为:
dis1 = fabs(cos(d) * (y[i] - y[j]) - sin(d) * (x[i] - x[j]))
dis2 = fabs(sin(d) * (y[i] - y[j]) + cos(d) * (x[i] - x[j]))
以上式子很好推导,自己画出坐标系,分析即可。
当然dis1和dis2大小不一定一致,要保证图形是覆盖所有点的正方形,所以我们选取较大者为边长。
精度问题:解决这个问题可以迭代M次,每次迭代把上一次得到的最优值附近再分为N份,重新旋转求最优值。取N=1000, M=10即可获得精确值。
//在0-90度范围内三分旋转的角度
//旋转公式:x’=x*cos(phi)-y*sin(phi) y’=x*sin(phi)+y*cos(phi)
#include
#include
#include
using namespace std;
struct point
{
double x,y;
}p[31];
int n;
const double pi=acos(-1.0);
const double eps=1e-4;
double area(double a)
{
return a*a;
}
double max(double a,double b)
{
return a>b?a:b;
}
//求面积
double cal(double z)
{
int i;
double x,y;
double max_x=-10000.0,max_y=-10000.0,min_x=10000.0,min_y=10000.0;
for(i=0;i
{
x=p[i].x*cos(z)-p[i].y*sin(z);
y=p[i].x*sin(z)+p[i].y*cos(z);
if(max_x
max_x=x;
if(max_y
max_y=y;
if(min_x>x)
min_x=x;
if(min_y>y)
min_y=y;
}
return max(area(max_x-min_x),area(max_y-min_y));
}
//三分
double div()
{
double left=0,right=pi/2.0;
double ans1,ans2;
double m,mm;
do
{
m=(left+right)/2.0;
mm=(right+m)/2.0;
ans1=cal(m);
ans2=cal(mm);
if(ans1
right=mm;
else
left=m;
}while(fabs(ans1-ans2)>eps);
return ans1;
}
int main(){
//freopen("C:\\Users\\Administrator\\Desktop\\001.txt","r",stdin);
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
int i;
for(i=0;i
scanf("%lf%lf",&p[i].x,&p[i].y);
printf("%.2lf\n",div());
}
return 0;
}