hdu 5251(凸包)

题意:
小度熊有一个桌面,小度熊剪了很多矩形放在桌面上,小度熊想知道能把这些矩形包围起来的面积最小的矩形的面积是多少。
题解:套用模板计算点集的最小包围矩形面积。
模板出自http://goaheadtw.iteye.com/blog/1195356

#include <stdio.h>
#include <string.h>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps = 1e-8;
const int N = 4005;

struct point{
    double x, y;
    point operator - (point d) {
        point dd;
        dd.x = this -> x - d.x;
        dd.y = this -> y - d.y;
        return dd;
    }
    point operator + (point d) {
        point dd;
        dd.x = this -> x + d.x;
        dd.y = this -> y + d.y;
        return dd;
    }
}p[N], ds[N];
int n;

int sign(double d) {
    return d < -eps ? -1 : (d > eps);
}

double dist(point d1, point d2){  
    return sqrt(pow(d1.x - d2.x, 2.0) + pow(d1.y - d2.y, 2.0));  
}
double dist2(point d1, point d2){  
    return pow(d1.x - d2.x, 2.0) + pow(d1.y - d2.y, 2.0);  
}
bool cmp(point d1, point d2){  
    return d1.y < d2.y || (d1.y == d2.y && d1.x < d2.x);  
}
//st1 --> ed1 叉乘 st2 --> ed2 的值
double xmul(point st1, point ed1, point st2, point ed2){  
    return (ed1.x - st1.x) * (ed2.y - st2.y) - (ed1.y - st1.y) * (ed2.x - st2.x);  
}  
double dmul(point st1, point ed1, point st2, point ed2){  
    return (ed1.x - st1.x) * (ed2.x - st2.x) + (ed1.y - st1.y) * (ed2.y - st2.y);  
}
//多边形类
struct poly{  
    static const int N = 4005; //点数的最大值  
    point ps[N + 5]; //逆时针储存多边形的点,[0, pn - 1]
    int pn; // 点数
    poly() { pn = 0; }  
    void push(point tp) { // 加入一个点
        ps[pn++] = tp;  
    }  
    int trim(int k) { //第k个位置 
        return (k + pn) % pn;
    }  
    void clear() { pn = 0; }  
};  
//返回含有n个点的点集ps的凸包
poly graham(point* ps, int n){  
    sort(ps, ps + n, cmp);  
    poly ans;  
    if(n <= 2){  
        for(int i = 0; i < n; i++){  
            ans.push(ps[i]);  
        }  
        return ans;  
    }  
    ans.push(ps[0]);  
    ans.push(ps[1]);  
    point* tps = ans.ps;  
    int top = -1;  
    tps[++top] = ps[0];  
    tps[++top] = ps[1];  
    for(int i = 2; i < n; i++){  
        while(top > 0 && xmul(tps[top - 1], tps[top], tps[top - 1], ps[i]) <= 0) top--;  
        tps[++top] = ps[i];  
    }  
    int tmp = top;
    for(int i = n - 2; i >= 0; i--){  
        while(top > tmp && xmul(tps[top - 1], tps[top], tps[top - 1], ps[i]) <= 0) top--;  
        tps[++top] = ps[i];  
    }  
    ans.pn = top;  
    return ans;  
}
//求点p到st->ed的垂足,列参数方程 
point getRoot(point p, point st, point ed){  
    point ans;  
    double u=((ed.x-st.x)*(ed.x-st.x)+(ed.y-st.y)*(ed.y-st.y));  
    u = ((ed.x-st.x)*(ed.x-p.x)+(ed.y-st.y)*(ed.y-p.y))/u;  
    ans.x = u*st.x+(1-u)*ed.x;  
    ans.y = u*st.y+(1-u)*ed.y;  
    return ans;  
}  
//next为直线(st,ed)上的点,返回next沿(st,ed)右手垂直方向延伸l之后的点  
point change(point st, point ed, point next, double l){  
    point dd;  
    dd.x = -(ed - st).y;  
    dd.y = (ed - st).x;  
    double len = sqrt(dd.x * dd.x + dd.y * dd.y);  
    dd.x /= len, dd.y /= len;  
    dd.x *= l, dd.y *= l;  
    dd = dd + next;  
    return dd;  
} 
//求含n个点的点集ps的最小面积矩形,并把结果放在ds(ds为一个长度是4的数组即可,ds中的点是逆时针的)中,并返回这个最小面积。
double getMinAreaRect(point* ps, int n, point* ds){
    int cn, i;
    double ans;
    point* con;
    poly tpoly = graham(ps, n);
    con = tpoly.ps;
    cn = tpoly.pn;
    if(cn <= 2){
        ds[0] = con[0]; ds[1] = con[1];
        ds[2] = con[1]; ds[3] = con[0];
        ans=0;
    }else{
        int  l, r, u;
        double tmp, len;
        con[cn] = con[0];
        ans = 1e40;
        l = i = 0;
        while(dmul(con[i], con[i+1], con[i], con[l])
            >= dmul(con[i], con[i+1], con[i], con[(l-1+cn)%cn])){
                l = (l-1+cn)%cn;
        }
        for(r=u=i = 0; i < cn; i++){
            while(xmul(con[i], con[i+1], con[i], con[u])
                <= xmul(con[i], con[i+1], con[i], con[(u+1)%cn])){
                    u = (u+1)%cn;
            }
            while(dmul(con[i], con[i+1], con[i], con[r])
                <= dmul(con[i], con[i+1], con[i], con[(r+1)%cn])){
                    r = (r+1)%cn;
            }
            while(dmul(con[i], con[i+1], con[i], con[l])
                >= dmul(con[i], con[i+1], con[i], con[(l+1)%cn])){
                    l = (l+1)%cn;
            }
            tmp = dmul(con[i], con[i+1], con[i], con[r]) - dmul(con[i], con[i+1], con[i], con[l]);
            tmp *= xmul(con[i], con[i+1], con[i], con[u]);
            tmp /= dist2(con[i], con[i+1]);
            len = xmul(con[i], con[i+1], con[i], con[u])/dist(con[i], con[i+1]);
            if(sign(tmp - ans) < 0){
                ans = tmp;
                ds[0] = getRoot(con[l], con[i], con[i+1]);
                ds[1] = getRoot(con[r], con[i+1], con[i]);
                ds[2] = change(con[i], con[i+1], ds[1], len);
                ds[3] = change(con[i], con[i+1], ds[0], len);
            }
        }
    }
    return ans + eps;
}

int main() {
    int t, cas = 1;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        n *= 4;
        for (int i = 0; i < n; i++)
            scanf("%lf%lf", &p[i].x, &p[i].y);
        printf("Case #%d:\n%d\n", cas++, (int)(getMinAreaRect(p, n, ds) + 0.5));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值