hdu 5251 矩形面积--2015百度之星初赛--旋转卡壳+凸包

题目:
小度熊有一个桌面,小度熊剪了很多矩形放在桌面上,小度熊想知道能把这些矩形包围起来的面积最小的矩形的面积是多少。
输入:
第一行一个正整数 T,代表测试数据组数(1≤T≤20),接下来 T 组测试数据。

每组测试数据占若干行,第一行一个正整数 N(1≤N<≤1000),代表矩形的数量。接下来 N 行,每行 8 个整数x1,y1,x2,y2,x3,y3,x4,y4,代表矩形的四个点坐标,坐标绝对值不会超过10000。
输出:
对于每组测试数据,输出两行:
第一行输出"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

我先说下这题的思路
求出最小矩形 由于矩形可能是倾斜的,所以不能通过直接判断 x最大点 x最小点 y最大点 y最小点来做
这个结果基本不会是最小的 比如样例 第一个样例跑出来就是20 不是倾斜的17
所以 这题就是凸包 + 旋转卡壳确定矩形
凸包好说,没有凸包连个规则多边形都没有,多余的点太多了,不需要
所以先建凸包,然后判断能把凸包包括进来的最小矩形
大量数学结论:

  1. 最后形成的矩形一定至少有一条边和凸包的边重合
    不证,自己想想.然后就可以通过枚举凸包每一条边来确定矩形的一条边
    既然矩形的一条边已经确认了,我们也可以通过while循环(旋转卡壳)分别求出离当前边最远的点,也就是确定凸包上的三个点,
    分别确定如果用当前这条边做矩形的一条边,那么我们往上至少要超过凸包哪一个点,往左至少超过哪一个,往右至少超过哪一个
    –>即确定上方,左方,右方的三个极限(不一定是端点,但是一定是矩形另外三条边上的点)
  2. 叉积可以求出两个向量组成的平行四边形的面积 A X B = A.xB.y+A.yB.x =|A||B|sin(angle) --> 除以2可以求三角形面积
    点积可以求出两个向量的投影长度 A·B = A.x
    B.x + A.y
    B.y = |A|*|B|*cos(angle) --> A在B上投影长度为 A·B/|B| -->注意绝对值
  3. 当前边一定的情况下,
    (1) 求最靠上的点 求当前边与凸包其他点构成的三角形面积,面积/底边长 = 高, 高最大时, 面积最大,也就是最靠上的点
    (2) 求最靠右的点 求投影长度,投影长度最长即为所求
    (3) 求最靠左的点 求投影长度,投影长度最长即为所求 注意的是,我采用的向量方向会导致这个结果为负数,所以,这个值越小,实际上长度越大
    更麻烦的是初始化的问题,在枚举第一条边的时候,必须令L=R,否则,若L=1,则L<=R,也就是叉积肯定小于R,我们就会认为此时判断L的投影长度较小是因为负数的关系,实际上是比较大的,从而停止while循环,导致结果错误

关于凸包的求法
这里用的是androw方法,更快一些

放代码

#include 
#include 
#include 
#include 
using namespace std;
#define eps 1e-6
const int N=4e3+10;
struct node
{
    int x,y;
}a[N],s[N];

int cnt;
node operator - (const node &A,const node &B){
    node t; t.x=A.x-B.x;t.y=A.y-B.y;
    return t;
}//这么写是为了过c++编译 , 直接return node{}也可以
int operator * (const node &A,const node &B){
    return A.x*B.y-A.y*B.x;
}//相当于叉积
bool cmp_point(node A,node B){
    if(A.x!=B.x) return A.x<B.x;
    return A.y<B.y;
}
void convex_hull(int n){
    //androw version
    sort(a,a+n,cmp_point);
    cnt=-1;
    for(int i=0;i<n;i++){//下凸包
        while(cnt>=1 && (a[i]-s[cnt-1])*(s[cnt]-s[cnt-1])>=0 ) cnt--;
        s[++cnt]=a[i];
    }
    int k=cnt;
    for(int i=n-2;i>0;i--){//上凸包
        while(cnt>k&& (a[i]-s[cnt-1])*(s[cnt]-s[cnt-1])>=0 ) cnt--;
        s[++cnt]=a[i];
    }
    
}
int dot(node A,node B){
    return A.x*B.x+A.y*B.y;
}
int roating(){
    
    double res=0x3f3f3f3f; cnt++;
    int l=1,r=1;
    for(int i=0,j=2;i<cnt;i++){
        while(abs( (s[j]-s[i])*(s[(i+1)%cnt]-s[i])) < abs( (s[(j+1)%cnt]-s[i])*(s[(i+1)%cnt]-s[i])) ) j=(j+1)%cnt;//确定高
        while( dot(s[(r+1)%cnt]-s[i],s[(i+1)%cnt]-s[i]) >= dot(s[r]-s[i],s[(i+1)%cnt]-s[i]) ) r=(r+1)%cnt;
        if(i==0) l=r;//大哥流批  流批啊 debug半天 原来是这么写的 服了服了
         while( dot(s[(l+1)%cnt]-s[i],s[(i+1)%cnt]-s[i]) <= dot(s[l]-s[i],s[(i+1)%cnt]-s[i]) ) l=(l+1)%cnt;
         
         double len1=fabs((double)dot(s[(i+1)%cnt]-s[i],s[(i+1)%cnt]-s[i])); len1=sqrt(len1);
         double len2=fabs((double)dot(s[r]-s[i],s[(i+1)%cnt]-s[i])/len1)-len1;//画图 投影包含i -> i+1 以及 i+1 -> r两部分 所以减去 
         double len3=fabs((double)dot(s[l]-s[i],s[(i+1)%cnt]-s[i])/len1); //画图 仅包含 i -> l  不用减
        double h=(s[j]-s[i])*(s[(i+1)%cnt]-s[i])/len1; if(h<eps) h=-h;
        res=min(res,(len1+len2+len3)*h);
    }
    return (int)(res+0.5);
}
int main(){

    int T;cin>>T;
    for(int cas=1;cas<=T;cas++){
        int n; cin>>n;
      
        for(int i=0;i<n*4;i++)
            cin>>a[i].x>>a[i].y;
        convex_hull(n*4);
        cout<<"Case #"<<cas<<":"<<endl<<roating()<<endl;
    }

    return 0;
}

你可能感兴趣的:(计算几何)