小度熊有一个桌面,小度熊剪了很多矩形放在桌面上,小度熊想知道能把这些矩形包围起来的面积最小的矩形的面积是多少。
每组测试数据占若干行,第一行一个正整数 N(1≤N<≤1000) ,代表矩形的数量。接下来 N 行,每行 8 个整数 x1,y1,x2,y2,x3,y3,x4,y4
第一行输出"Case #i:",i 代表第 i 组测试数据。
第二行包含1 个数字,代表面积最小的矩形的面积,结果保留到整数位。
2 2 5 10 5 8 3 10 3 8 8 8 8 6 7 8 7 6 1 0 0 2 2 2 0 0 2
Case #1: 17 Case #2: 4
这是好久之前写的题然后好久没碰旋转卡壳就都忘了QAQ。。。。
这个题先求这N个小矩形的凸包再用旋转卡壳求出覆盖这个凸包的最小矩形面积
roll() 函数即是旋转卡壳了
对于找覆盖凸包的最小矩形可以这样写:
这个矩形必有至少一条边与凸包的某一条边重合,
在凸包上遍历这条边,根据这条边在凸包上找到与之对应的最上面的点 p1,和最左点和最右点 p2,p3,这些点都是凸包的顶点
然后矩形的长宽分别是 p1 到这条边的距离以及过 p2,p3 所做的与这条边垂直的一对平行线之间的距离
根据长宽求面积取最小值
emmmmm..................................
代码:
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cstdio>
#include <set>
#include <cmath>
#include <map>
#include <algorithm>
#define INF 0x3f3f3f3f
#define MAXN 10000005
#define Mod 10001
using namespace std;
typedef long long LL;
#define point pair<double,double>
#define x first
#define y second
double dot(point o,point a,point b)
{
return (a.x-o.x)*(b.x-o.x)+(a.y-o.y)*(b.y-o.y);
}
double cross(point o,point a,point b)
{
return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
}
double dist(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double dist(point o,point a,point b)
{
return fabs(cross(o,a,b))/dist(a,b);
}
double dist(point o1,point o2,point a,point b)
{
double l=dist(a,b);
return l+fabs(dot(b,a,o1))/l+fabs(dot(a,b,o2))/l;
}
point p[4000+233];
int n,s[4000+233],top;
bool cmp(point p1,point p2) //极角排序函数 , 角度相同则距离小的在前面
{
int tmp=cross(p[0],p1,p2);
if(tmp>0) return true;
else if(tmp==0&&dist(p[0],p1)<dist(p[0],p2)) return true;
else return false;
}
void init() //输入,并把 最左下方的点放在 list[0] 。并且进行极角排序
{
int i,k;
point p0;
scanf("%lf%lf",&p[0].x,&p[0].y);
p0.x=p[0].x;
p0.y=p[0].y;
k=0;
for(i=1;i<n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
if( (p0.y>p[i].y) || ((p0.y==p[i].y)&&(p0.x>p[i].x)) )
{
p0.x=p[i].x;
p0.y=p[i].y;
k=i;
}
}
p[k]=p[0];
p[0]=p0;
sort(p+1,p+n,cmp);
}
void graham()
{
int i;
if(n==1) {top=0;s[0]=0;}
if(n==2)
{
top=1;
s[0]=0;
s[1]=1;
}
if(n>2)
{
for(i=0;i<=1;i++) s[i]=i;
top=1;
for(i=2;i<n;i++)
{
while(top>0&&cross(p[s[top-1]],p[s[top]],p[i])<=0) top--;
top++;
s[top]=i;
}
}
}
double ans=1e16;
point a[4000+233];
void roll()
{
ans=1e16;
for(int i=0;i<=top;i++)
{
a[i]=p[s[i]];
}
top++;
a[top]=p[s[0]];
int pos1=0,pos2=1;//pos1,pos2表示在凸包上遍历那条边,根据这条边找可以覆盖凸包的矩形
int p1=1,p2=1,p3=1;//分别是相对于边 <pos1,pos2> 最上最左最右的点(p2,p3哪个左哪个右我忘了)
for(;pos1<top;)
{
while(fabs(cross(a[pos2],a[pos1],a[p1]))<=fabs(cross(a[pos2],a[pos1],a[(p1+1)%top])))
{
p1++;
p1%=top;
}
while(dot(a[pos2],a[pos1],a[p2])>=dot(a[pos2],a[pos1],a[(p2+1)%top]))
{
p2++;
p2%=top;
}
if(pos1==0)p3=p2;
while(dot(a[pos2],a[pos1],a[p3])<=dot(a[pos2],a[pos1],a[(p3+1)%top]))
{
p3++;
p3%=top;
}
double h=dist(a[p1],a[pos1],a[pos2]);
double l=dist(a[p2],a[p3],a[pos1],a[pos2]);
// printf("\npos1: %.0f %.0f\n",a[pos1].x,a[pos1].y);
// printf("pos2: %.0f %.0f\n",a[pos2].x,a[pos2].y);
// printf("p1: %.0f %.0f\n",a[p1].x,a[p1].y);
// printf("p2: %.0f %.0f\n",a[p2].x,a[p2].y);
// printf("p3: %.0f %.0f\n",a[p3].x,a[p3].y);
ans=min(ans,h*l);
pos1++;
pos2++;
}
}
int main()
{
int T,cas=0;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
n*=4;
init();
graham();
// for(int i=0;i<=top;i++)
// {
// printf("%.0f %.0f\n",p[s[i]].x,p[s[i]].y);
// }
roll();
printf("Case #%d:\n%.0f\n",++cas,ans);
}
return 0;
}
本人蒟蒻,如有错误,还望指正