P4636 [SHOI2011] 直线拟合

题意

\(n\) 个点,求一条直线,使得祂和离祂最远的点的距离最小,求这个最小值。
\(n\le10^5\)

思路

把凸包建出来,对于每条凸包上的边,找到以这条边为底,与其祂点构成的三角形中高的最大值。答案就是所有最大高中最小值除以 \(2\)。这个是好求的,直接用旋转卡壳。

代码

要注意判断凸包中所有点共线的情况,否则会死循环。

// Problem: P4636 [SHOI2011] 直线拟合
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4636
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(double&x){scanf("%lf",&x);}
    inline void write(double x){printf("%lf",x);}
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=100010;
const double eps=1e-5;
int n;
struct point{
    double x,y;
    bool operator<(const point t)const{
        if(abs(x-t.x)<eps) return y<t.y;
        return x<t.x;
    }
}a[maxn];
vector<int>hull;
bool check(point a,point b,point c){return (b.y-a.y)*(c.x-a.x)+eps>(c.y-a.y)*(b.x-a.x);}
bool check(int a,int b,int c){return check(::a[a],::a[b],::a[c]);}
void find_hull(){
    hull.push_back(1);
    for(int i=2;i<=n;i++){
        while(hull.size()>=2&&check(hull[hull.size()-2],hull.back(),i)) hull.pop_back();
        hull.push_back(i);
    }
    int nwsz=hull.size();
    for(int i=n-1;i>=1;i--){
        while(hull.size()>nwsz&&!check(i,hull.back(),hull[hull.size()-2])) hull.pop_back();
        hull.push_back(i);
    }
    hull.pop_back();
}
double get_dis(point a,point b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
double get_dis(point a,point b,point c){
    double fm=(b.y-a.y)*c.x-(b.x-a.x)*c.y+b.x*a.y-b.y*a.x;
    fm=abs(fm);
    return fm/get_dis(a,b);
}
double get_dis(int a,int b,int c){return get_dis(::a[a],::a[b],::a[c]);}
signed main(){
    read(n);
    for(int i=1;i<=n;i++) read(a[i].x,a[i].y);
    sort(a+1,a+1+n);
    find_hull();
    if(hull.size()==2){write("0.00");return 0;}
    bool gx=1;
    for(int i=2;i<hull.size();i++) if(get_dis(hull[0],hull[1],hull[i])>eps) gx=0;
    if(gx){write("0.00");return 0;}
    double ans=INFINITY;
    int w=0;
    auto get_nxt=[](int x){return (x+1)%hull.size();};
    for(int i=0;i<hull.size();i++){
        while(get_dis(hull[i],hull[get_nxt(i)],hull[w])<get_dis(hull[i],hull[get_nxt(i)],hull[get_nxt(w)])+eps) w=get_nxt(w);
        ans=min(ans,get_dis(hull[i],hull[get_nxt(i)],hull[w]));
    }
    printf("%.2lf",ans/2);
    return 0;
}
posted @ 2026-05-18 17:46  Link-Cut_Trees  阅读(10)  评论(0)    收藏  举报