凸包Graham扫描法->HDU3847

本文介绍了使用Graham扫描法解决凸包问题,详细阐述了算法步骤,并提供了HDU3847题目的具体应用,该题目要求求解图形通过缝隙的最小宽度,可以通过构建凸包并计算点到边的最大距离来解决。
摘要由CSDN通过智能技术生成

Graham扫描法求凸包

凸包定义:

点集Q的凸包(convex hull)是指一个最小凸多边形,满足Q中的点或者在多边形边上或者在其内。

凸包最常用的凸包算法是Graham扫描法和Jarvis步进法。

Graham扫描法:

首先,找到所有点中最左边的(y坐标最小的),如果y坐标相同,找x坐标最小的.

以这个点为基准求所有点的极角(atan2(y-y0,x-x0)),并按照极角对这些点排序,前述基准点在最前面,设这些点为P[0]..P[n-1].

PS:这样预处理后,保证p[0],p[1]和p[n-1]都是凸包上的点.

建立一个栈,初始时P[0]、P[1]、P[2]进栈,对于 P[3..n-1]的每个点,若栈顶的两个点与它不构成”向左转”的关系,则将栈顶的点出栈,直至没有点需要出栈以后将当前点进栈;
所有点处理完之后栈中保存的点就是凸包了。

图示:
这里写图片描述


HDU3847

题意:

求能让一个给出的图形通过缝隙的最小宽度。

题解:

用这些点构造一个凸包,枚举凸包的各个边,求凸包上其他点到这个边的距离的最大值中的最小值。

代码:

#include <stdio.h>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <float.h>
using namespace std ;
#define MAX 110
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(){}
    Point(double _x , double _y)
    {
        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 ;
    }
};
struct Line
{
    Point s , e ;
    Line(){}
    Line(Point _s , Point _e)
    {
        s = _s ; e = _e ;
    }
};
double xmult(Point p0,Point p1,Point p2) //叉积p0p1 X p0p2
{
    return (p1-p0)^(p2-p0);
}
double dist(Point a ,Point b)
{
    return sqrt((b - a) * (b - a)) ;
}
double PointToLine(Point p ,Line L)
{
    Point result ;
    double t = ((p-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));
    result.x=L.s.x+(L.e.x-L.s.x)*t ;
    result.y=L.s.y+(L.e.y-L.s.y)*t ;
    return dist(p , result) ;
}
Point list[MAX] ;
int Stack[MAX] , top ;
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 anglesort(int n) //输入,并把最左下方的点放在list[0],并且进行极角排序
{
    int i,k;
    Point p0;
    scanf("%lf%lf",&list[0].x,&list[0].y);
    p0.x=list[0].x;
    p0.y=list[0].y;
    k=0;
    for(i=1;i<n;i++)
    {
        scanf("%lf%lf",&list[i].x,&list[i].y);
        if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)) )
        {
            p0.x=list[i].x;
            p0.y=list[i].y;
            k=i;
        }
    }
    list[k]=list[0];
    list[0]=p0;

    sort(list+1,list+n,_cmp);
}
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;
    }
    //for(int i = 0 ; i < top ; i ++) printf("%d\n" , Stack[i]);
}
double solve()
{
    Line ll ;
    double x , y , ans , temp;
    ans = DBL_MAX ;
    ll = Line(list[Stack[0]] , list[Stack[top - 1]]) ;
    for(int k = 0 ; k < top ; k ++)
    {
        if(k != 0 && k != top-1)
            temp = max(temp , PointToLine(list[Stack[k]] , ll)) ;
    }
    ans = min(ans , temp) ;
    for(int i = 0 ; i < top - 1 ; i ++)
    {
        int j = i + 1 ;
        ll = Line(list[Stack[i]] , list[Stack[j]]) ;
        temp = 0.0 ;
        for(int k = 0 ; k < top ; k ++)
        {
            if(k != i && k != j)
                temp = max(temp , PointToLine(list[Stack[k]] , ll)) ;
                //cout << temp << endl ;
        }
        ans = min(ans , temp) ;
    }
    return ans ;
}
int main()
{
    int n , cas = 1 , len;
    while(scanf("%d" , &n)!=EOF , n)
    {
        anglesort(n) ;
        Graham(n) ;
        printf("Case %d: %.2f\n",cas++, solve() + 0.005);
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值