POJ1113 计算几何雏形:凸包!

这里写图片描述

今天pty大神给我们讲了计算几何的基本,能几乎听懂的感觉还是不错的,但不能调出来一道模板题的话,总不能感觉掌握了。嗯,就找了这道堪称计算几何的模板来做了一下,发现,嗯,是我小看了。。。一上午和一下午啊。。的确是有写点东西的必要了。
老样子首先说明一下题意,那个,给你一座城堡,让你在城堡周围修一堵围墙,距离城堡的距离为k,嗯。乍一看有点古怪,其实好好看一下图就知道了,就是城堡的周长加上了一个圆的周长,因为围着城堡绕一圈正好赚了360度对吧,那不就正好一个圆吗,,那就只用考虑城堡的周长就行了嗯。
城堡是通过拐角的点给出的,换言之就是要通过给出的点求出城堡的周长,就是说包围着这些点的“凸包”,求凸包咯!
具体来说怎么做呐,就是GRAHAM求法,即排序+查找;
首先是排序,把所有的点排一遍序,点排序的依据是极角(马上解释),首先找到所有点中左下地方的那个点,就是纵坐标最低,如果纵坐标一样就比较横坐标,找较小的那一个,可以证明这样的一个点一定在凸包的“壳”上,那么就把这么个点记为0号点,从这个点开始找凸壳吧!,找点的代码:

    point p0;
    scanf("%d%d",&a[0].x,&a[0].y);
    p0.x=a[0].x,p0.y=a[0].y;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
        if((p0.y>a[i].y)||(p0.y==a[i].y)&&(p0.x>a[i].x))
        {
            p0.x=a[i].x,p0.y=a[i].y;
            pos=i;
        }
    }
    a[pos]=a[0];
    a[0]=p0;

这里的p0只是个临时变量,作用是找到最“矮”的点并把它存到a0里,嗯,就是这样。
然后就是我们的排序了,用快排方便加快捷,但cmp函数要自己打:

bool cmp(const point &aa,const point &b)
{
    int ans=cj(a[0],aa,b);
    if(ans>0) return true;
    else if(ans==0&&dis(a[0],aa)<dis(a[0],b)) return true;
    else return false;
}

来解释一下,这里呢,是极角排序,什么是极角呐,就是以我们选定的a0为源点,建立平面直角坐标系,每一个点与源点的连线,与y轴的夹角大小排序,夹角越大,排序越小,这样就能保证依次遍历图时,能够从右边绕到左边来,嗯。但是呐,a0的值最好不要参与排序,因为其他都是按极角排序的,a0是按位置确定的,这样能避免a0的重复计算,嗯。
然后就是关键的一步了,找凸壳!要分一下类:
如果只有一个点的话,凸壳也无所谓了
有2个点的话,凸壳就只有一条边而已,入栈就行了。
有2个以上的边的话,先依次入栈,如果向左转(叉积>0)的话,直接入栈就行了,否则(叉积<0)向右转时就不是凸包了,就把栈弹出,弹到栈首点能够向左转到达时,就把枚举的点入栈!代码:

if(n>2)
    {
        stack[0]=a[0],stack[1]=a[1];
        for(int i=2;i<n;i++)
        {
            while(top>0&&cj(stack[top-1],stack[top],a[i])<=0)   top--;
            stack[++top]=a[i];
        }
    }

如此,待全部的点都遍历完了后,保存在栈里的点就是凸壳了,把他们的距离依次加起来即可,代码:

for(int i=0;i<=top;i++)     
        sum+=dis(stack[i],stack[(i+1)%(top+1)]);   

呐,完整代码:

#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const double PI=acos(-1.0);
struct point
{
    int x,y;
}a[100010],stack[100010];
double sum=0;
int n,k;
double dis(point a,point b)
{
    return sqrt((double)(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int cj(point p0,point p1,point p2) 
{
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}    
bool cmp(const point &aa,const point &b)
{
    int ans=cj(a[0],aa,b);
    if(ans>0) return true;
    else if(ans==0&&dis(a[0],aa)<dis(a[0],b)) return true;
    else return false;
}
int main()
{
    int pos=0;
    scanf("%d%d",&n,&k);
    point p0;
    scanf("%d%d",&a[0].x,&a[0].y);
    p0.x=a[0].x,p0.y=a[0].y;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
        if((p0.y>a[i].y)||(p0.y==a[i].y)&&(p0.x>a[i].x))
        {
            p0.x=a[i].x,p0.y=a[i].y;
            pos=i;
        }
    }
    a[pos]=a[0];
    a[0]=p0;
    int top=1;
    sort(a+1,a+n,cmp);
    if(n == 1)
    {
        top=0;stack[0]=a[0];
    }
    if(n == 2)
    {
        top=1;
        stack[0]=a[0];
        stack[1]=a[1];
    }
    if(n>2)
    {
        stack[0]=a[0],stack[1]=a[1];
        for(int i=2;i<n;i++)
        {
            while(top>0&&cj(stack[top-1],stack[top],a[i])<=0)   top--;
            stack[++top]=a[i];
        }
    }
    for(int i=0;i<=top;i++)     
        sum+=dis(stack[i],stack[(i+1)%(top+1)]);   
    printf("%d",(int)(sum+2*PI*k+0.5));
}

如何,不长吧?关于叉积判断方向,就请自己了解下吧,我的算叉积的函数也是直接套用的叉积的公式,证明和推论请参考百度百科!嗯嗯。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值