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

该博客介绍了如何通过凸包和旋转卡壳算法解决找到能包围多个矩形的最小矩形面积问题。具体步骤包括构建凸包,然后通过枚举凸包边来确定矩形的一条边,并利用叉积和点积计算找到其他三条边的极限点。文中还提到了初始化时避免错误的方法,并提供了使用Andrew方法实现凸包的代码示例。
摘要由CSDN通过智能技术生成

题目:
小度熊有一个桌面,小度熊剪了很多矩形放在桌面上,小度熊想知道能把这些矩形包围起来的面积最小的矩形的面积是多少。
输入:
第一行一个正整数 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 <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值