UVA 11168(凸包算法)

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=34780

Problem

给N个点,画一条直线,所有点都在直线一侧(可以在直线上),且到达直线的平均距离最小(实际上就是总距离最小)。输出平均距离,N有10000级别,点坐标挺大。

Solution

首先,求一个凸包,枚举凸包点边,O(1)求出所有点到边的距离,维护最小值即可。

首先,为啥枚举凸包的边是对的?

其次,怎么O(1)求出,设直线返程为Ax+By+C=0, 点(x0,y0)到直线的距离为:Ax0+By0+CA2+B2√,由于点都在直线一侧,所以(Ax0+By0+C)的符号都是一样的,只需预处理出∑ni=1xi和∑ni=1yi,就好办啦。

怎么把求两点的一般式?最美的求法。

设y=kx+b,k=y1?y2x1?x2,b=y1-k*x1,kx-y+b=0,则A=k=y1?y2x1?x2,B=-1,C=b=y1?y1?y2x1?x2?x1,都乘上(x1-x2),得到:A=y1-y2,B=x2-x1,C=x1*y2-y1*x2(叉积,美不美?美不美!………..<–有病)

My code

//Hello. I‘m Peter.
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef unsigned int uin;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define MAXN
#define N 10100
#define M
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
const double eps=1e-9;
int dcmp(double x){
    if(fabs(x)<eps) return 0;
    else if(x<0) return -1;
    else return 1;
}
int n;
struct Point{
    double x,y;
    Point(){};
    Point(double xx,double yy){
        x=xx,y=yy;
    }
};
inline void readPointInt(Point &p){
    int x,y;
    x=read(),y=read();
    p=Point(x,y);
}
typedef Point Vector;
Vector operator+(const Vector a,const Vector b){
    return Vector(a.x+b.x,a.y+b.y);
}
Vector operator -(const Vector a,const Vector b){
    return Vector(a.x-b.x,a.y-b.y);
}
double operator *(const Vector a,const Vector b){
    return a.x*b.x+a.y*b.y;
}
double operator %(const Vector a,const Vector b){
    return a.x*b.y-a.y*b.x;
}
Vector operator*(const Vector a,const double b){
    return Vector(a.x*b,a.y*b);
}
Vector operator*(const double b,const Vector a){
    return Vector(a.x*b,a.y*b);
}
bool operator ==(const Vector a,const Vector b){
    return !dcmp(a.x-b.x)&&!dcmp(a.y-b.y);
}
inline bool comp(const Point a,const Point b){
    if(dcmp(a.x-b.x)) return a.x<b.x;
    else return a.y<b.y;
}
#define lenp res.size()
void Convex_Hull(Point *p,int n,vector<Point>&res){
    res.clear();
    sort(p,p+n,comp);
    for(int i=0;i<n;i++){
        while(lenp>1&&dcmp((res[lenp-1]-res[lenp-2])%(p[i]-res[lenp-1]))<=0) res.pop_back();
        res.push_back(p[i]);
    }
    int k=(int)lenp;
    for(int i=n-2;i>=0;i--){
        while(lenp>k&&dcmp((res[lenp-1]-res[lenp-2])%(p[i]-res[lenp-1]))<=0) res.pop_back();
        res.push_back(p[i]);
    }
    if(lenp>1) res.pop_back();
}
Point p[N];
double A,B,C;
inline void calABC(Point p1,Point p2){
    A=p1.y-p2.y;
    B=p2.x-p1.x;
    C=p1%p2;
}
int main(){
    int T=read();
    for(int kase=1;kase<=T;kase++){
        printf("Case #%d: ",kase);
        n=read();
        double totalx=0,totaly=0;
        for(int i=0;i<n;i++){
            readPointInt(p[i]);
            totalx+=p[i].x;
            totaly+=p[i].y;
        }
        if(n<=2){
            double ans=0.00000;
            printf("%.3f\n",ans);
            continue;
        }
        vector<Point>res;
        Convex_Hull(p,n,res);
        res.push_back(res[0]);//小技巧
        double ansdis=LL;
        for(int i=0;i<lenp-1;i++){
            calABC(res[i],res[i+1]);
            double up=fabs(A*totalx+B*totaly+C*n);
            double down=sqrt(A*A+B*B);
            double totaldis=up/down;
            if(!i) ansdis=totaldis;
            else ansdis=fmin(ansdis,totaldis);
        }
        printf("%.3f\n",ansdis/n);
    }
    return 0;
}
时间: 2024-08-15 23:05:23

UVA 11168(凸包算法)的相关文章

UVa 11168 (凸包+点到直线距离) Airport

题意: 平面上有n个点,求一条直线使得所有点都在直线的同一侧.并求这些点到直线的距离之和的最小值. 分析: 只要直线不穿过凸包,就满足第一个条件.要使距离和最小,那直线一定在凸包的边上.所以求出凸包以后,枚举每个边求出所有点到直线的距离之和得到最小值. 点到直线距离公式为: 因为点都在直线同一侧,所以我们可以把加法“挪”到里面去,最后再求绝对值,所以可以预处理所有点的横坐标之和与纵坐标之和.当然常数C也要记得乘上n倍. 已知两点坐标求过该点直线的方程,这很好求不再赘述,考虑到直线没有斜率的情况,

UVa 11168 Airport , 凸包

题意: 给出平面上n个点,找一条直线,使得所有点在直线的同侧,且到直线的距离之平均值尽量小. 先求凸包 易知最优直线一定是凸包的某条边,然后利用点到直线距离公式进行计算. #include<cstdio> #include<cstring> #include<vector> #include<cmath> #include<algorithm> #include<iostream> using namespace std; struc

计算几何-凸包算法 Python实现与Matlab动画演示

凸包算法是计算几何中的最经典问题之一了.给定一个点集,计算其凸包.凸包是什么就不罗嗦了 本文给出了<计算几何——算法与应用>中一书所列凸包算法的Python实现和Matlab实现,并给出了一个Matlab动画演示程序. 啊,实现谁都会实现啦╮(╯▽╰)╭,但是演示就不一定那么好做了. 算法CONVEXHULL(P)  输入:平面点集P  输出:由CH(P)的所有顶点沿顺时针方向组成的一个列表 1.   根据x-坐标,对所有点进行排序,得到序列p1, …, pn 2.   在Lupper中加入p

Graham Scan凸包算法

获得凸包的算法可以算是计算几何中最基础的算法之一了.寻找凸包的算法有很多种,Graham Scan算法是一种十分简单高效的二维凸包算法,能够在O(nlogn)的时间内找到凸包. 首先介绍一下二维向量的叉积(这里和真正的叉积还是不同的):对于二维向量a=(x1,y2)和b=(x2,y2),a×b定义为x1*y2-y1*x2.而它的几何意义就是|a||b|sin<a,b>.如果a与b夹角小于180度(逆时针),那么这个值就是正值,大于180度就是负值.需要注意的是,左乘和右乘是不同的.如图所示:

Airport UVA - 11168

Airport UVA - 11168 1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 10010; 4 const int inf = 0x3f3f3f3f; 5 const int eps = 1e-12; 6 7 struct Point { 8 double x, y; 9 Point (double x = 0, double y = 0) : x(x), y(y) {} 10 }; 11 ty

openlayer的凸包算法实现

最近在要实现一个openlayer的凸多边形,也遇到了不小的坑,就记录一下 1.具体的需求: 通过在界面点击,获取点击是的坐标点,来绘制一个凸多边形. 2.思考过程: 1)首先,我们得先获取点击事件发生时,触发的点的坐标 map.events.register('click', map, function (e) { var pixel = new OpenLayers.Pixel(e.xy.x,e.xy.y); var lonlat = map.getLonLatFromPixel(pixel

线段余弦角+凸包算法

/// /// 根据余弦定理求两个线段夹角 /// /// 端点 /// start点 /// end点 /// double Angle(PointF o, PointF s, PointF e) { double cosfi = 0, fi = 0, norm = 0; double dsx = s.X - o.X; double dsy = s.Y - o.Y; double dex = e.X - o.X; double dey = e.Y - o.Y; cosfi = dsx * de

凸包算法

先理解下凸包 说凸包首先要说凸性的定义,简单点说就是平面邻域中任意两点所在的线段上的点都在该邻域中,则该邻域具有凸性.简单推敲一下,就可以发现如果邻域中存在一阶导数不连续的点一定无法被某点集线性表示出来.再往下的内容属于数学分析了,对我们的算法设计帮助不大,暂时先不管. 一般的计算几何问题都是处理的离散点集形成的平面域,所以我们感兴趣的是怎样找一个包含这个点集的面积最小的凸多边形,这就是凸包.作为常识也应该知道凸包上的顶点必然是该点集的子集,所以根据此性质我们就可以设计高效算法. 下面将介绍三种

平面上圆的凸包算法

平面上圆的凸包算法 我们之前探讨过这个有趣的问题: 平面上有若干圆,求包含这些圆的所有凸集的交. 根据之前讨论的结果,直接按圆心排序做扫描线的方法是错误的.我们需要考虑圆上的每一个点是否可以作为凸包上的一部分. 然而圆上的点的数目是无限多的.我们需要借助离散化的思想:因为我们发现凸包一定是由若干圆弧和线段构成的.而其中线段一定是切线段.由此,我们只需要将每两两圆的切点取出来求一个凸包.显然,在圆凸包上出现的切线段一定会在切点凸包中出现:而切点凸包中其余的线段则一定是弧弦. 但是,这个算法需要枚举

凸包算法的应用——数一数图形中共有多少三角形

一.问题引入 网络上经常会遇到判断图形个数的题目,如下例: 如果我们要把图中所有三角形一个一个选出来,在已知每个交点的前提下,该如何用代码判断我们选的图形是否是三角形呢.如下图,如何把图3筛选出来呢? 这里需要用到两步: 1.得到所选图形(阴影部分)所包含的所有小图形的顶点集合,求集合的凸包,根据凸包顶点个数判定凸包围成的图形是否是三角形,若顶点个数不为3则不是三角形,如图(1). .2.若凸包围成的图形是三角形,判断凸包的面积与所选图形(所有选中的小图形面积之和)是否相等,若相等则所选图形是三