bzoj 3707: 圈地 计算几何

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33229466/article/details/81603666

题意

2维平面上有n个木桩,黄学长有一次圈地的机会并得到圈到的土地,为了体现他的高风亮节,他要使他圈到的土地面积尽量小。圈地需要圈一个至少3个点的多边形,多边形的顶点就是一个木桩,圈得的土地就是这个多边形内部的土地。(因为黄学长非常的神,所以他允许圈出的第n点共线,那样面积算0)
n 1000

分析

先考虑枚举两个点,然后以这两个点所在直线为y轴旋转下坐标系,那么显然离y轴最近的两个点才会对答案有贡献。
考虑如何维护这个旋转坐标系的过程,我们可以先把点按x排序,然后把点对按斜率从小到大处理,那么每次处理到一个点对时,先把这两点的贡献计算一下,在之后的过程中,显然仅有这两个点的相对位置必然会发生变化,那么就直接交换这两个点的位置即可。

代码

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

const int N=1005;
const double inf=1e18;

int n,tot,pos[N],id[N];
struct point{double x,y;}p[N];
struct data{int x,y;double w;}a[N*N];

bool cmpx(point a,point b)
{
    return a.x<b.x;
}

bool cmpw(data a,data b)
{
    return a.w<b.w;
}

double cross(point a,point b,point c)
{
    return fabs((a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x));
}

double calc(int i,int j,int k)
{
    return cross(p[i],p[j],p[k]);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
    std::sort(p+1,p+n+1,cmpx);
    for (int i=1;i<n;i++)
        for (int j=i+1;j<=n;j++)
        {
            a[++tot].x=i;a[tot].y=j;
            a[tot].w=(p[j].y-p[i].y)/(p[j].x-p[i].x);
        }
    std::sort(a+1,a+tot+1,cmpw);
    double ans=inf;
    for (int i=1;i<=n;i++) pos[i]=id[i]=i;
    for (int i=1;i<=tot;i++)
    {
        int x=a[i].x,y=a[i].y;
        if (pos[x]>pos[y]) std::swap(x,y);
        if (pos[x]>1) ans=std::min(ans,calc(x,y,id[pos[x]-1]));
        if (pos[y]<n) ans=std::min(ans,calc(x,y,id[pos[y]+1]));
        std::swap(pos[x],pos[y]);
        std::swap(id[pos[x]],id[pos[y]]);
    }
    printf("%.2lf",ans/2);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33229466/article/details/81603666