题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5251
开始没想到用凸包,看了大佬的博客才明白。
思路:矩形各边必经过凸包上的点,并且矩形一边为凸包上的边,另外三边经过凸包上的点。先枚举矩形的一个边(即凸包上的边),再确定另外三点。矩形为高低左右四边,假设此边为为低边(相对意义上的),再确定另外三边上的三个点即可。与此边距离最大的点是高边上的点,利用叉积来求,因为三角形底确定,高就是距离,面积越大,距离越大;在此边最右边的点为 右边上的点,利用点积来求,因为点积反映了b向量在a向量方向上的投影,点积越大,越右;左边上的点也用点积来求。确定了边和三点,之后算面积。
和往常一样,利用旋转卡壳是凸函数的性质来优化时间。
确定三点时,判别式用大于等于或小于等于而非大于或小于。
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
const int MAXN=4005;
const double PI=acos(-1.0);
int flag;
struct point
{
int x,y;
};
point list[MAXN];
int stack[MAXN],top;
int cross(point p0,point p1,point p2) //计算叉积 p0p1 X p0p2
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
int dotproduct(point p0,point p1,point p2) //计算点积 p0p1 * p0p2
{
int x1=p1.x-p0.x;
int x2=p2.x-p0.x;
int y1=p1.y-p0.y;
int y2=p2.y-p0.y;
return x1*x2+y1*y2;
}
double dis(point p1,point p2) //计算 p1p2的 距离
{
return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
double dis1(point p1,point p2) //计算 p1p2的 距离平方
{
return ((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(point p1,point p2) //极角排序函数 , 角度相同则距离小的在前面
{
int tmp=cross(list[0],p1,p2);
if(tmp>0) return true;
else if(tmp==0&&dis(list[0],p1)<dis(list[0],p2)) return true;
else return false;
}
void init(int n) //输入,并把 最左下方的点放在 list[0] 。并且进行极角排序
{
int i,k;
point p0;
p0.x=list[0].x;
p0.y=list[0].y;
k=0;
for(i=1;i<n;i++)
{
if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)) )
{
p0.x=list[i].x;
p0.y=list[i].y;
k=i;
}
}
list[k]=list[0];
list[0]=p0;
sort(list+1,list+n,cmp);
//printf("&&\n");
}
void graham(int n)
{
int i;
if(n==1) {top=0;stack[0]=0;}
if(n==2)
{
top=1;
stack[0]=0;
stack[1]=1;
}
if(n>2)
{
for(i=0;i<=1;i++) stack[i]=i;
top=1;
for(i=2;i<n;i++)
{
while(top>0&&cross(list[stack[top-1]],list[stack[top]],list[i])<=0) top--;
top++;
stack[top]=i;
}
stack[++top]=0;
}
/*for(int i=0;i<=top;i++)
{
printf("%d %d*",list[stack[i]].x,list[stack[i]].y);
}
printf("&&\n");*/
}
int main()
{
int t,l,r,h,n,x;
double sum;
scanf("%d",&t);
x=1;
while(t--)
{
scanf("%d",&n);
n=n*4;
for(int i=0;i<n;i++)
{
scanf("%d%d",&list[i].x,&list[i].y);
}
init(n);
graham(n);
l=r=h=1;
for(int i=0;i<top;i++)
{
while(cross(list[stack[i]],list[stack[i+1]],list[stack[h+1]])>=
cross(list[stack[i]],list[stack[i+1]],list[stack[h]]))/// 叉积 不要将等号去掉 沙雕了
h=(h+1)%top;
while(dotproduct(list[stack[i]],list[stack[i+1]],list[stack[r+1]])>=
dotproduct(list[stack[i]],list[stack[i+1]],list[stack[r]]))///点积
r=(r+1)%top;
if(i==0) l=r;
while(dotproduct(list[stack[i]],list[stack[i+1]],list[stack[l+1]])<=
dotproduct(list[stack[i]],list[stack[i+1]],list[stack[l]]))///点积
l=(l+1)%top;
double d=dis1(list[stack[i]],list[stack[i+1]]);
if(i==0)
{
sum=(double)fabs(cross(list[stack[i]],list[stack[i+1]],list[stack[h]]))*
fabs(dotproduct(list[stack[i]],list[stack[i+1]],list[stack[r]])-dotproduct(list[stack[i]],list[stack[i+1]],list[stack[l]]))/d;
}
else
{
double sum1=(double)(cross(list[stack[i]],list[stack[i+1]],list[stack[h]]))*
fabs(dotproduct(list[stack[i]],list[stack[i+1]],list[stack[r]])-dotproduct(list[stack[i]],list[stack[i+1]],list[stack[l]]))/d;
if(sum1<sum)
sum=sum1;
}
}
long long ans=sum+0.5;
printf("Case #%d:\n",x);
printf("%lld\n",ans);
x++;
}
return 0;
}