BZOJ 3707: 圈地 (动态凸包)

传送门

题意:
n 个点,求最小三角形。(n1000)

题解:
好题啊。。

(后面有动态凸包)
有一种简单的做法:
首先如果选定了底边的两点,那么还需要选择一条离这条底边最近的点。
将当前坐标系y轴旋转这条底边,那么选择 |X| 最小的点。

考虑按照斜率从小到大枚举底边。
对于两个点 a,b 来说,若一条底边 c,d 的斜率小于 a,b 斜率,且在 c,d 的坐标系下 ax>bx ,那么在所有斜率小于 a,b 的坐标系下都满足 ax>bx 。所有斜率大于 a,b 的底边都满足 bx>ax 。那么一开始建立一个序列,每次枚举到 a,b 边就交换 a,b 位置就可以保证序列一直有序,查找可以做到 O(1) 。总时间复杂度 O(n2logn)

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f; 
}
const int Maxn=1e3+50;
const double INF=1e18;
int n,tot,pos[Maxn];
double ans=INF;
struct point{
    double x,y;
    int id;
    point(double x=0,double y=0):x(x),y(y){} 
    friend inline bool operator <(const point &a,const point &b){
        return a.x<b.x; 
    }
    friend inline point operator -(const point &a,const point &b){
        return point(a.x-b.x,a.y-b.y);
    } 
    friend inline double operator *(const point &a,const point &b){
        return a.x*b.y-a.y*b.x;
    }
}p[Maxn];
struct line{
    int a,b;
    double slope; 
    friend inline bool operator <(const line &a,const line &b){
        return a.slope<b.slope;
    }
}l[Maxn*Maxn];
inline double calc(const point &a,const point &b,const point &c){
    return fabs((a-b)*(a-c));
}
int main(){
    n=read();
    for(int i=1;i<=n;i++)
        p[i].x=read(),p[i].y=read(),p[i].id=i;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            l[++tot]=(line){i,j,(p[i].x==p[j].x)?INF:(p[i].y-p[j].y)/(p[i].x-p[j].x)};
    sort(l+1,l+tot+1);sort(p+1,p+n+1);
    for(int i=1;i<=n;i++)pos[p[i].id]=i;
    for(int i=1;i<=tot;i++){
        int a=l[i].a,b=l[i].b;
        swap(pos[a],pos[b]),swap(p[pos[a]],p[pos[b]]);
        if(pos[a]>1&&pos[a]-1!=pos[b])ans=min(ans,calc(p[pos[a]],p[pos[b]],p[pos[a]-1]));
        if(pos[a]<n&&pos[a]+1!=pos[b])ans=min(ans,calc(p[pos[a]],p[pos[b]],p[pos[a]+1]));
        if(pos[b]>1&&pos[b]-1!=pos[a])ans=min(ans,calc(p[pos[a]],p[pos[b]],p[pos[b]-1]));
        if(pos[b]<n&&pos[b]+1!=pos[a])ans=min(ans,calc(p[pos[a]],p[pos[b]],p[pos[b]+1]));
    }
    printf("%.2f\n",ans/2.0);
} 

动态凸包:
考虑按照x轴依次加入点,将所有横坐标比他大的点按照到这个点的极角排序,考虑所有包含当前点的三角形必然是由这个点到右边某个点的直线加上一个最近距离点构成的,依次加入点之后在凸包上找就行了。时间复杂度 O(n2logn) (代码是真的难调,可读性可能比较低。。)

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f; 
}
const int Maxn=1e3+50;
const double INF=1e18;
const double eps=1e-9;
int n,now;
double ans=INF;
struct point{
    double x,y;
    double slope;
    point(double x=0,double y=0,double slope=0):x(x),y(y),slope(slope){} 
    friend inline point operator -(const point &a,const point &b){return point(a.x-b.x,a.y-b.y);} 
    friend inline double operator *(const point &a,const point &b){return a.x*b.y-a.y*b.x;}
    inline double norm(){return sqrt(x*x+y*y);}
}p[Maxn],tp[Maxn],tp2[Maxn];
#define calc(a,b,c) (fabs((a-b)*(c-b)))
inline bool cmpx(const point &a,const point &b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
inline bool cmpslope(const point &a,const point &b){
    if(a.slope!=b.slope)return a.slope<b.slope;
    else return a.y<b.y;
}
struct cmp_x{
    inline bool operator ()(const point &a,const point &b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
};
inline int sign(const double x){
    return (x>eps)-(x<-eps);
}

struct cmp_slope{
    inline bool operator ()(const point &a,const point &b){
        return sign(a.slope - b.slope) > 0;
    }
};
set<point,cmp_x>S_x;
set<point,cmp_slope>S_t;
typedef set<point>::iterator it;
inline point findsuf(const point &a){
    it t=S_t.lower_bound(a);
    if(t==S_t.end()||sign(t->slope-a.slope)<0)
        --t;
    return *t;
}
inline void ins(const point &a){
    it suf=S_x.lower_bound(a),pre=suf;
    int insbz=0; 
    if(suf!=S_x.begin()&&suf!=S_x.end()&&((*suf-a)*(*--pre-a)>=0))return;
    if(suf!=S_x.begin()){
        if(suf==S_x.end())--pre;
        while(pre!=S_x.begin()){
            it t=pre;--t;
            if((*t-a)*(*pre-a)>=0){
                S_t.erase(S_t.find(*pre));
                S_x.erase(pre);
                pre=t;
            }
            else break;
        }
        point tmp=a;tmp.slope=atan2(a.y-pre->y,a.x-pre->x);
        S_x.insert(tmp);S_t.insert(tmp);    
    }else insbz=1;
    if(suf!=S_x.end()){
        while(suf!=--S_x.end()){
            it t=suf;++t;
            if((*suf-a)*(*t-a)>=0){
                S_t.erase(S_t.find(*suf));
                S_x.erase(suf);
                suf=t;
            }
            else break;
        }
        point tmp=*suf;
        S_t.erase(S_t.find(*suf));
        S_x.erase(suf);
        tmp.slope=atan2(tmp.y-a.y,tmp.x-a.x);
        S_t.insert(tmp);S_x.insert(tmp);
    }
    if(insbz){
        point tmp=point(a.x,a.y,INF);
        S_x.insert(tmp);
        S_t.insert(tmp);
    }
}
int main(){
    n=read();
    for(int i=1;i<=n;i++)p[i].x=read(),p[i].y=read();
    sort(p+1,p+n+1,cmpx);
    for(now=1;now<n-1;now++){
        memcpy(tp+now+1,p+now+1,sizeof(point)*(n-now));
        for(int i=now+1;i<=n;i++)tp[i].slope=(atan2(tp[i].y-p[now].y,tp[i].x-p[now].x));
        sort(tp+now+1,tp+n+1,cmpslope);
        S_x.clear();S_t.clear();
        point t(tp[now+1].x,tp[now+1].y,INF);
        S_x.insert(t);S_t.insert(t);
        for(int i=now+2;i<=n;i++){
            t=findsuf(point(0,0,atan2(tp[i].y-p[now].y,tp[i].x-p[now].x)));
            ans=min(ans,calc(t,tp[i],p[now]));
            ins(tp[i]);
        }
    }
    printf("%.2f\n",ans/2.0);
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值