计算几何

此文章是一些自己未接触过的计算几何知识,不是太明白。。。。。

zoj 1450 && hdu 3007     本题代码来自 himdd大牛  :http://blog.himdd.com/archives/2666

/**
    最小圆覆盖 (随机增量法)
    求给你n个点,让你用一个最小的圆覆盖所有的点,求这个圆的坐标&&半径
**/
#include <iostream>
#include <cstdio>
#include <ctime>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps = 1e-12;
const int len = 100010;
struct Point
{
    double x,y;
} p[len];
struct Line
{
    Point a,b;
};
int dbcmp(double n)
{
    return n < -eps ? -1 : n > eps;
}
double dis(Point a, Point b)
{
    return sqrt((a.x-b.x) * ( a.x-b.x) + ( a.y-b.y) * ( a.y-b.y));
}

//求两直线的交点
Point intersection(Line u,Line v)
{
    Point ret=u.a;
    double t=((u.a.x-v.a.x)*(v.b.y-v.a.y)-(u.a.y-v.a.y)*(v.b.x-v.a.x))
             /((u.a.x-u.b.x)*(v.b.y-v.a.y)-(u.a.y-u.b.y)*(v.b.x-v.a.x));
    ret.x+=(u.b.x-u.a.x)*t;
    ret.y+=(u.b.y-u.a.y)*t;
    return ret;
}
//三角形外接圆圆心(外心)
Point center(Point a,Point b,Point c)
{
    Line u,v;
    u.a.x=(a.x+b.x)/2;
    u.a.y=(a.y+b.y)/2;
    u.b.x=u.a.x+(u.a.y-a.y);
    u.b.y=u.a.y-(u.a.x-a.x);
    v.a.x=(a.x+c.x)/2;
    v.a.y=(a.y+c.y)/2;
    v.b.x=v.a.x+(v.a.y-a.y);
    v.b.y=v.a.y-(v.a.x-a.x);
    return intersection(u,v);
}

void min_cir(Point * p, int n, Point & cir, double &r)
{
    random_shuffle(p, p + n);
    cir = p[0];
    r = 0;
    for(int i = 1; i < n; ++i)
    {
        if(dbcmp(dis(p[i],cir) -r) <= 0)continue;
        cir = p[i];
        r = 0;
        for(int j =0; j < i ; ++j)
        {
            if(dbcmp(dis(p[j], cir) -r) <= 0 )continue;
            cir.x = (p[i].x + p[j].x) /2;
            cir.y = (p[i].y + p[j].y) /2;
            r = dis( cir, p[j]);
            for(int k =0; k < j; ++k)
            {
                if(dbcmp( dis(p[k], cir) -r) <= 0) continue;
                cir = center(p[i], p[j], p[k]);
                r = dis( cir, p[k]);
            }

        }
    }
}
int main()
{
    int n;
    while( ~scanf("%d", &n), n)
    {
        for (int i = 0; i < n; ++i)
        {
            scanf("%lf%lf", &p[i].x, &p[i].y);
        }
        Point cir;
        double r;
        min_cir(p, n, cir, r);
        printf("%.2lf %.2lf %.2lf\n", cir.x, cir.y, r);
    }

}


POJ 3608 Bridge Across Islands

题意:求两凸包之间的距离;摘自 kuangbin 大牛:http://www.cnblogs.com/kuangbin/p/3221911.html


#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;

const double eps = 1e-8;
int sgn(double x)
{
    if(fabs(x) < eps)return 0;
    if(x < 0)return -1;
    else return 1;
}
struct Point{
    double x,y;
    Point(double _x = 0.0,double _y = 0.0){
        x = _x;y = _y;
    }
    Point operator -(const Point &b)const{
        return Point(x - b.x, y - b.y);
    }
    double operator ^(const Point &b)const{
        return x*b.y - y*b.x;
    }
    double operator *(const Point &b)const{
        return x*b.x + y*b.y;
    }
    void input(){
        scanf("%lf%lf",&x,&y);
    }
};
struct Line
{
    Point s,e;
    Line(){}
    Line(Point _s,Point _e){
        s = _s;
        e = _e;
    }
};
double dist(Point a,Point b){
    return sqrt((a-b)*(a-b));
}
Point NearestPointToLineSeg(Point P,Line L){
    Point result;
    double t = ((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));
    if(t >= 0 && t <= 1){
        result.x = L.s.x + (L.e.x - L.s.x)*t;
        result.y = L.s.y + (L.e.y - L.s.y)*t;
    }
    else{
        if(dist(P,L.s) < dist(P,L.e))
            result = L.s;
        else result = L.e;
    }
    return result;
}
/*
 * 求凸包,Graham算法
 * 点的编号0~n-1
 * 返回凸包结果Stack[0~top-1]为凸包的编号
 */
const int MAXN = 10010;
Point list[MAXN];
int Stack[MAXN],top;
//相对于list[0]的极角排序
bool _cmp(Point p1,Point p2){
    double tmp = (p1-list[0])^(p2-list[0]);
    if(sgn(tmp) > 0)return true;
    else if(sgn(tmp) == 0 && sgn(dist(p1,list[0]) - dist(p2,list[0])) <= 0)
        return true;
    else return false;
}
void Graham(int n){
    Point p0;
    int k = 0;
    p0 = list[0];
    //找最下边的一个点
    for(int i = 1;i < n;i++){
        if( (p0.y > list[i].y) || (p0.y == list[i].y && p0.x > list[i].x) ){
            p0 = list[i];
            k = i;
        }
    }
    swap(list[k],list[0]);
    sort(list+1,list+n,_cmp);
    if(n == 1){
        top = 1;
        Stack[0] = 0;
        return;
    }
    if(n == 2){
        top = 2;
        Stack[0] = 0;
        Stack[1] = 1;
        return ;
    }
    Stack[0] = 0;
    Stack[1] = 1;
    top = 2;
    for(int i = 2;i < n;i++){
        while(top > 1 && sgn((list[Stack[top-1]]-list[Stack[top-2]])^(list[i]-list[Stack[top-2]])) <= 0)
            top--;
        Stack[top++] = i;
    }
}
//点p0到线段p1p2的距离
double pointtoseg(Point p0,Point p1,Point p2){
    return dist(p0,NearestPointToLineSeg(p0,Line(p1,p2)));
}
//平行线段p0p1和p2p3的距离
double dispallseg(Point p0,Point p1,Point p2,Point p3){
    double ans1 = min(pointtoseg(p0,p2,p3),pointtoseg(p1,p2,p3));
    double ans2 = min(pointtoseg(p2,p0,p1),pointtoseg(p3,p0,p1));
    return min(ans1,ans2);
}
//得到向量a1a2和b1b2的位置关系
double Get_angle(Point a1,Point a2,Point b1,Point b2){
    Point t = b1 - ( b2 - a1 );
    return (a2-a1)^(t-a1);
}
//旋转卡壳,求两个凸包的最小距离
double rotating_calipers(Point p[],int np,Point q[],int nq){
    int sp = 0, sq = 0;
    for(int i = 0;i < np;i++)
        if(sgn(p[i].y - p[sp].y) < 0)
            sp = i;
    for(int i = 0;i < nq;i++)
        if(sgn(q[i].y - q[sq].y) > 0)
            sq = i;
    double tmp;
    double ans = 1e99;
    for(int i = 0;i < np;i++){
        while(sgn(tmp = Get_angle(p[sp],p[(sp+1)%np],q[sq],q[(sq+1)%nq])) < 0 )
            sq = (sq + 1)%nq;
        if(sgn(tmp) == 0)
            ans = min(ans,dispallseg(p[sp],p[(sp+1)%np],q[sq],q[(sq+1)%nq]));
        else ans = min(ans,pointtoseg(q[sq],p[sp],p[(sp+1)%np]));
        sp = (sp+1)%np;
    }
    return ans;
}

double solve(Point p[],int n,Point q[],int m){
    return min(rotating_calipers(p,n,q,m),rotating_calipers(q,m,p,n));
}
Point p[MAXN],q[MAXN];
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)==2)
    {
        if(n == 0 && m == 0)break;
        for(int i = 0;i < n;i++)
            list[i].input();
        Graham(n);
        n = top;
        for(int i = 0;i < n;i++)
            p[i] = list[Stack[i]];
        for(int i = 0;i < m;i++)
            list[i].input();
        Graham(m);
        m = top;
        for(int i = 0;i < m;i++)
            q[i] = list[Stack[i]];
        printf("%.5lf\n",solve(p,n,q,m));
    }
    return 0;
}


































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值