bzoj1185 [HNOI2007]最小矩形覆盖

1185: [HNOI2007]最小矩形覆盖

Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special Judge
Submit: 1945  Solved: 853
[Submit][Status][Discuss]

Description

 

分析:有一个比较显然的结论:最小的矩形一定有一条边在凸包上.利用旋转卡壳求出对应边的最远点.这两个是比较容易做到的,有点难的是如何求另外两边的最远点.

          另外两个点的位置显然是单峰函数,可以利用旋转卡壳的思想.关键就是如何以当前凸包的边为x轴来找最远的点,这里不能单单只找横坐标最大/小的点.利用点积,就能将边投影到凸包的边上去,也就是比较点积的大小.那么思路基本上就出来了:底边固定了,最高点利用叉积(旋转卡壳)来确定,左右两个点用点积来确定.最后求矩形的长和高,利用叉积求高,点积求长.求四个顶点的位置则利用向量的伸缩(比例).

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const double eps = 1e-10;

int n,tot;
double maxx = 1e12;
struct node
{
    double x,y;
    node() {}
    node(double _x,double _y) :x(_x),y(_y) {}
} e[50010],ans[10],p[50010];

double dist(node a,node b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

double det(node a,node b)  //叉积
{
    return a.x * b.y - a.y * b.x;
}

double dot(node a,node b)  //点积
{
    return a.x * b.x + a.y * b.y;
}

node operator + (node a,node b)
{
    return node(a.x + b.x,a.y + b.y);
}

node operator - (node a,node b)
{
    return node(a.x - b.x,a.y - b.y);
}

node operator * (node a,double x)
{
    return node(a.x * x,a.y * x);
}

int three(double x)  //三态函数
{
    if (fabs(x) < eps)
        return 0;
    if (x > eps)
        return 1;
    return -1;
}

bool operator < (node a,node b)  //输出比较
{
    if (three(a.y - b.y) == 0)
    {
        if (three(a.x - b.x) == -1)
            return true;
        return false;
    }
    if (three(a.y - b.y) == -1)
        return true;
    return false;
}

bool cmp(node a,node b)
{
    double temp = det(a - e[1],b - e[1]);
    if (temp != 0)
        return temp > 0;
    return dist(a,e[1]) < dist(b,e[1]);
}

void solve()  //求凸包
{
    int id = 1;
    for (int i = 1; i <= n; i++)
        if (three(e[i].x - e[id].x) == -1 || (three(e[i].x - e[id].x) == 0 && three(e[i].y - e[id].y) == -1))
            id = i;
    if (id != 1)
        swap(e[1],e[id]);
    sort(e + 2,e + 1 + n,cmp);
    p[++tot] = e[1];
    for (int i = 2; i <= n; i++)
    {
        while (tot >= 2 && det(e[i] - p[tot - 1],p[tot] - p[tot - 1]) >= 0)
            tot--;
        p[++tot] = e[i];
    }
}

void solve2()
{
    p[tot + 1] = p[1];
    int left = 1,right = 1,up = 1;
    for (int i = 1; i <= tot; i++)
    {
        while (three(fabs(det(p[up + 1] - p[i + 1],p[i] - p[i + 1])) - fabs(det(p[up] - p[i + 1],p[i] - p[i + 1]))) >= 0)
            up = up % tot + 1;  //最高点
        while (three(dot(p[i + 1] - p[right + 1],p[i] - p[i + 1]) - dot(p[i + 1] - p[right],p[i] - p[i + 1])) >= 0)
            right = right % tot + 1;   //最右边的点
        if (i == 1)
            left = right;
        while (three(dot(p[i] - p[left + 1],p[i + 1] - p[i]) - dot(p[i] - p[left],p[i + 1] - p[i])) >= 0)
            left = left % tot + 1;
        double D = dist(p[i],p[i + 1]);
        double H = det(p[up] - p[i + 1],p[i] - p[i + 1]) / D;
        double L = fabs(dot(p[i + 1] - p[i],p[left] - p[i + 1])) / D + fabs(dot(p[i + 1] - p[i],p[right] - p[i + 1])) / D; //底边
        if (three(L * H - maxx) == -1)
        {
            maxx = L * H;
            ans[0] = p[i] + (p[i + 1] - p[i]) * ((fabs(dot(p[i + 1] - p[i],p[right] - p[i + 1])) / D + D) / D);
            ans[1] = ans[0] + (p[right] - ans[0]) * (H / dist(p[right],ans[0]));
            ans[2] = ans[1] + (p[up] - ans[1]) * (L / dist(p[up],ans[1]));
            ans[3] = ans[2] + (p[left] - ans[2]) * (H / dist(p[left],ans[2]));
        }
    }
}

int main()
{
    scanf("%d",&n);
    for (int i = 1; i <= n; i++)
        scanf("%lf%lf",&e[i].x,&e[i].y);
    solve();
    solve2();
    int id = 0;
    for (int i = 1; i < 4; i++)
        if (ans[i] < ans[id])
            id = i;
    printf("%.5lf\n",maxx);
    for (int i = 0; i < 4; i++)
        printf("%.5lf %.5lf\n",ans[(i + id) % 4].x,ans[(i + id) % 4].y);

    return 0;
}

 

posted @ 2017-12-27 14:55  zbtrs  阅读(236)  评论(0编辑  收藏  举报