UVa11168 - Airport(凸包+点到直线的距离)

题目链接

简介:
找到一条直线,使得所有点在直线的同侧,且到直线的距离之和尽量小

分析:
显然,所求直线不能穿过凸包,也不能与凸包相离,
所以只能与凸包上的边重合,因为凸包上最多有n条边,如果我们能在O(1)的时间内算出所有点到直线的距离之和,这个问题我们就可以在O(n)的时间内解决了

首先我们明确,一个点到直线的距离怎么求

  • 用向量的方法
    d=Cross(P-A,B-A)/Len(B-A)
    这虽然是新学的操作,但是对于这道题并不适用

  • 解析几何
    点到直线的距离:
    因为所有点都在直线的同一侧,所有的Ax+By+C符号相同
    所以我们可以直接把分子的绝对值先去掉,加起来之后再取绝对值
    |(Ax1+By1+C)+(Ax2+By2+C)+…+(Axn+Byn+C)|
    =|A(x1+x2+…+xn)+B(y1+y2+…+yn)+n*C|
    这样只要预处理出x,y坐标之和即可

在这里提一句,我们怎么把两个点确定的直线化成Ax+By+C=0 的形式呢
当然是画柿子啦:
这里写图片描述

tip

最后输出平均距离

刚开始WA一次,看了看题目n>0,又把n=1考虑一下

但是得到的又是WA
于是我就开始对拍啊,发现如果是单组数据,我的程序是对的,
当T>1的时候,我的程序就有WA的几率,
最后才发现是因为输入没有完成的时候,我就特判了n=1和n=2
导致之后的数据会读入错误

//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>

using namespace std;

const double eps=1e-8;
const int N=20010;
int n,sta[N];
double A,B,C;
struct node{
    double x,y;
    node (double xx=0,double yy=0)
    {
        x=xx;y=yy;
    }
};
node po[N];

node operator + (const node &a,const node &b){return node(a.x+b.x,a.y+b.y);}
node operator - (const node &a,const node &b){return node(a.x-b.x,a.y-b.y);}
node operator * (const node &a,const double &b){return node(a.x*b,a.y*b);}
node operator / (const node &a,const double &b){return node(a.x/b,a.y/b);}
bool operator < (const node &a,const node &b){return a.x<b.x||(a.x==b.x && a.y<b.y);}
bool operator == (const node &a,const node &b){return a.x==b.x && a.y==b.y;}

double Cross(node x,node y){return x.x*y.y-x.y*y.x;}

int dcmp(double x)
{
    if (fabs(x)<eps) return 0;
    else if (x>0) return 1;
    else return -1; 
}

int TuB(int n)
{
    sort(po+1,po+1+n);
    int top=0;
    for (int i=1;i<=n;i++)
    {
        while (top>1&& dcmp( Cross(po[sta[top]]-po[sta[top-1]] , po[i]-po[sta[top-1]]) )<=0) top--;
        sta[++top]=i;
    }
    int k=top;
    for (int i=n-1;i>=1;i--)
    {
        while (top>k&& dcmp( Cross(po[sta[top]]-po[sta[top-1]] , po[i]-po[sta[top-1]]) )<=0) top--;
        sta[++top]=i;
    }
    if (n>1) top--;
    return top;
}

void getline(node x,node y)
{
    A=x.y-y.y;
    B=y.x-x.x;
    C=x.x*y.y-x.y*y.x;
    return;
}

int main()
{
    int T;
    scanf("%d",&T);
    for (int cas=1;cas<=T;cas++)
    {
        scanf("%d",&n);

        double X=0,Y=0;
        for (int i=1;i<=n;i++)
        {
            scanf("%lf%lf",&po[i].x,&po[i].y);
            X+=po[i].x;
            Y+=po[i].y;
        }

        if (n==2||n==1)
        {
            printf("Case #%d: 0.000\n",cas);
            continue;
        }

        int m=TuB(n);
        double ans=1e16;
        sta[0]=sta[m];
        for (int i=1;i<=m;i++)
        {
            getline(po[sta[i-1]],po[sta[i]]);
            double a=fabs(A*X+B*Y+n*C)/(sqrt(A*A+B*B));
            if (dcmp(ans-a)>0) ans=a;
        }

        printf("Case #%d: %0.3lf\n",cas,ans/n);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值