BZOJ3707 圈地

原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3707

圈地

Description

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

Input

第一行一个整数n,表示木桩个数。
接下来n行,每行2个整数表示一个木桩的坐标,坐标两两不同。

Output

仅一行,表示最小圈得的土地面积,保留2位小数。

Sample Input

3

0 0

0 1

1 0

Sample Output

0.50

HINT

对于100%的数据,n<=1000。

题解

每次更新最小三角形时,我们不用O(n)枚举,事实上我们只需要找到离当前线段最近的两个点就行了。那怎么找这两个点呢,我们只需要维护一个“从左到右”排好序的序列就好了。先以y轴为基准排序,之后O(n^2)枚举出所有可能的边,再以这些边为基准旋转坐标系,就能从序列中O(1)找出线段左右的两点,因为每次旋转时,实际上只有作为基准线段的两点在序列中的位置发生了变化,只需要交换两点即可。  
另外,我们可以将平面分为若干块,在块内O(n^3)暴力求最小三角形,显然正确性堪忧,所以随机旋转坐标系若干次。。。期望得分100。

代码
#include<bits/stdc++.h>
using namespace std;
const int M=1005;
const double eps=1e-8;
int sig(double x){return (x>eps)-(x<-eps);}
struct pt{int x,y;};
struct li{int x,y;double k;};
bool operator < (pt a,pt b){return a.x<b.x;}
bool operator < (li a,li b){return a.k<b.k;}
pt operator - (pt a,pt b){return (pt){a.x-b.x,a.y-b.y};}
double operator * (pt a,pt b){return a.x*1.0*b.y-a.y*1.0*b.x;}
int n,pos[M],rank[M],tot;
double ans=1e200;
pt p[M];
li line[M*M*5];
void in()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    scanf("%d%d",&p[i].x,&p[i].y);
    sort(p+1,p+1+n);
}
void ml()
{
    for(int i=1;i<=n;++i)pos[i]=rank[i]=i;
    for(int i=1;i<n;++i)
    for(int j=i+1;j<=n;++j)
    {
        line[++tot].x=i;line[tot].y=j;
        line[tot].k=(p[j].y-p[i].y)/(double)(p[j].x-p[i].x);
    }
    sort(line+1,line+1+tot);
}
void ac()
{
    int t1,t2;
    for(int i=1;i<=tot;++i)
    {
        t1=line[i].x;t2=line[i].y;
        if(pos[t1]>pos[t2])swap(t1,t2);
        if(pos[t1]>1)ans=min(ans,fabs((p[t1]-p[t2])*(p[rank[pos[t1]-1]]-p[t2])));
        if(pos[t2]<n)ans=min(ans,fabs((p[t1]-p[t2])*(p[rank[pos[t2]+1]]-p[t2])));
        swap(pos[t1],pos[t2]);
        swap(rank[pos[t1]],rank[pos[t2]]);
    }
    printf("%.2lf",ans/2.0);
}
int main()
{
    in();
    ml();
    ac();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值