2019牛客暑期多校训练营(第十场)G Road Construction 不算几何的思维几何

题目链接: https://ac.nowcoder.com/acm/contest/890/G

题意:

给你300个二维平面上的点,要你找出一条直线使得,所有点到这条直线的最小距离最大,要你输出这个最大的最小距离。

做法:

这道题在题解里面给出了一个“优美”的结论: 最优的直线一定平行或垂直于两个点的连线。 这是为什么呢,我们考虑这个最短的距离 m i n d i s mindis mindis,如果只是和一个点 p i p_{i} pi 的距离,那么我们还可以经过平移使得这条线远离这个点,增大最小距离,直到到了和另一个点 p j p_{j} pj 的距离相等,并且再平移之后会使得两个点中的任一一个点的距离变小,这个时候的答案就是我们想要的了,垂直情况也是很容易考虑的,只有两个点就是了嘛对叭。

这个时候题解发话:枚举最优直线斜率(最多有 n 2 n^2 n2 个),然后用 k t h − e l e m e n t k_{th}-element kthelement 找到以这条直线排序的中间两个点,答案即两个点到该直线的距离差除以2。

我相信肯定有人看到这个 k t h − e l e m e n t k_{th}-element kthelement 的时候和我一样懵…什么意思…然后这个时候我就要贴出供我理解的图了。
2019牛客暑期多校训练营(第十场)G Road Construction 不算几何的思维几何_第1张图片
相信枚举斜率这件事情大家都能懂了,但是要怎么计算答案呢。在上面的图中,我们能看得出,在任一一个斜率之下,我们都已知所有点的坐标,那么根据直线 y = k x + b y = kx+b y=kx+b 我们就可以得到 b i = y i − k x i b_{i}=y_{i}-kx_{i} bi=yikxi ,在这个斜率下,我们得到了所有的常数 b i b_{i} bi ,从上面的图中我们能看得出比例关系吧,那就除以 s q r t ( k 2 + 1 ) sqrt(k^{2}+1) sqrt(k2+1) 就是两个点在这个斜率下的距离了,所以我们只要找到中间两个的距离差除以2就好啦!

代码

#include
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
const int maxn = 305;
typedef long long ll;
double x[maxn],y[maxn];
double tmp[maxn],ans;
int n;
void Cal(double k){
    rep(i,1,n) tmp[i]=(y[i]-k*x[i])/sqrt(k*k+1);
    sort(tmp+1,tmp+1+n);
    ans=min(ans,(tmp[n/2]-tmp[n/2+1])/2);
}
int main(){
    ans=1e9;
    scanf("%d",&n);
    rep(i,1,n) scanf("%lf%lf",&x[i],&y[i]);

    for(int i=2;i<=n;i++){
        for(int j=1;j<i;j++){
            if(x[i]==x[j]&&y[i]==y[i]) continue;
            double k=(y[i]-y[j])/(x[i]-x[j]);
            Cal(k);
            k=-1.0/k;
            Cal(k);
        }
    }
    sort(x+1,x+1+n);
    sort(y+1,y+1+n);
    ans=min(ans,(x[n/2]-x[n/2+1])/2);
    ans=min(ans,(y[n/2]-y[n/2+1])/2);
    printf("%.12f\n",fabs(ans));
    return 0;
}

你可能感兴趣的:(思维,计算几何)