关于数论【凸包】

凸包,算是计算几何的知识了,今天补了一发。

在一个平面坐标系上有n个点,用笔画一个多边形,使得多边形包含这n个点(点在多边形的边上也算包含)。求多边形的最小周长。

这就是要你求一个凸包的周长。

那我们如何得到一个凸包的所有顶点呢?可以发现,这个凸包肯定是用部分给出的顶点连接,包含其他全部的点。具体的做法,就是先找到一个最下最左的点,以他为基准点,按逆时针的方向,将其他的点排序,然后弄栈,保证新加入的点一定在逆时针方向(保证凹包不出现)否则踢栈顶,因为加入的新点和次栈顶所连线肯定包含栈顶。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct point
{
    double x,y;
}p[1100],sta[1100];int top;
double gougu(point p1,point p2){return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));}
double multi(point p1,point p2,point p0)
{
    double x1,y1,x2,y2;
    x1=p1.x-p0.x;
    y1=p1.y-p0.y;
    x2=p2.x-p0.x;
    y2=p2.y-p0.y;
    return x1*y2-x2*y1;
}
bool cmp(point p1,point p2)
{
    double t=multi(p1,p2,p[1]);
    if(t<0||(t==0&&gougu(p1,p[1])>gougu(p2,p[1])))return false;
    return true;
}
int n;
void garham()
{
    sort(p+2,p+n+1,cmp);//按逆时针放点
    top=2;sta[1]=p[1];sta[2]=p[2];
    for(int i=3;i<=n;i++)
    {
        while(top>1&&multi(sta[top],p[i],sta[top-1])<=0)top--;
        //判断一下以top-1为基准点,新来的点p[i]在栈顶的点哪边,如果在顺时针方向,那么说明p[i]可以包含到它,那就踢栈顶 
        sta[++top]=p[i];
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf",&p[i].x,&p[i].y);
        if(p[1].y>p[i].y||(p[1].y==p[i].y&&p[1].x>p[i].x))//确定最下最左的点 
        {
            swap(p[1],p[i]);
        }
    }
    garham();
    double ans=0.0;
    for(int i=1;i<top;i++)ans+=gougu(sta[i],sta[i+1]);
    ans+=gougu(sta[top],sta[1]);
    printf("%.4lf",ans);
    return 0;
}

 

posted @ 2017-09-27 21:09  AKCqhzdy  阅读(181)  评论(0编辑  收藏  举报