您的位置:首页 > 其它

poj 3348 Cows 凸包模板题

2014-07-20 12:34 274 查看
题意:给定n个点,求这n个点构成的凸包的面积。

题解:典型的凸包模板题。先求m个点组成的凸包,然后根据凸包上的一个点,将凸包分成m-2个三角形,面积就是m-2个三角形之和。三角形面积等于两条边的叉积/2.

凸包求法讲解:

1)先将点根据横坐标从小到大排序

2)然后需要了解叉积。根据两个向量的叉积,我们可以知道一条边是在另一条边的上方还是下方。

3)接着先求凸包的下半部分。遍历点,每新进来一个点,跟已求得的凸包边比较,用叉积Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2]),判断新加点是在已成凸包的外面还是里面。若是在外面,则删除所有边,当边向量和该边的起点到新增点所称向量叉积小于0,即删除在凸包内的点;将新增点加入到凸包数组中,形成新的凸包。以此类推得到凸包下边。

具体过程如下图所示,开始P1,P2,P3,P4,P6都在凸包数组中,现在加入P7;由于P4P6在P4P7上方(用叉积判),所以删除P6;由于P3P4在P3P7上方,所以删除P4;由于P2P3在P2P7下方,所以停止删除,将P7加入到凸包数组中,得到新的凸包数组P1,P2,P3,P7。接着遍历下一个点,如此反复,直到遍历所有的点,就可以得到凸包的下半部分了。

4)跟3)同理得到凸包上边,最后得到的数组就是凸包了。



代码:

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
using namespace std;
//基础点和向量运算
struct Point{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Vector operator + (Vector A,Vector B){return Vector(A.x+B.x,A.y+B.y);}
Vector operator - (Vector A,Vector B){return Vector(A.x-B.x,A.y-B.y);}
Vector operator * (Vector A,double p){return Vector(A.x*p,A.y*p);}
Vector operator / (Vector A,double p){return Vector(A.x/p,A.y/p);}
bool operator <(const Point& a, const Point& b)
{
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double eps=1e-10;
int dcmp(double x)//判断正负,或者等于0
{
    if(fabs(x)<eps)return 0;else return x<0?-1:1;
}
bool operator==(const Point& a,const Point &b)
{
    return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}
double Dot(Vector A, Vector B){return A.x*B.x+A.y*B.y;}//点积
double Length(Vector A){return sqrt(Dot(A,A));}//OA长
double Angle(Vector A,Vector B){return acos(Dot(A,B)/Length(A)/Length(B));}//OA和OB的夹角
double Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}//叉积
double Area2(Point A,Point B,Point C){return Cross(B-A,C-A);}//三角形面积
Vector Rotate(Vector A,double rad)//rad为弧度,旋转rad度
{
    return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));
}
Vector Normal(Vector A)//A的单位法向量,A不能为零向量
{
    double L=Length(A);
    return Vector(-A.y/L,A.x/L);
}

//点和直线
//P+tv表示一条直线,P为点,tv为方向向量
Point GetLineIntersection(Point P,Vector v,Point Q,Vector w)//求直线交点,确保存在交点,即Cross(v,w)非0
{
    Vector u=P-Q;
    double t=Cross(w,u)/Cross(v,w);
    return P+v*t;
}
double DistanceToLine(Point P,Point A,Point B)//P点到直线AB的距离
{
    Vector v1=B-A,v2=P-A;
    return fabs(Cross(v1,v2)/Length(v1));
}
double DistanceToSegment(Point P,Point A,Point B)//点P到线段AB的距离
{
    if(A==B)return Length(P-A);
    Vector v1=B-A,v2=P-A,v3=P-B;
    if(dcmp(Dot(v1,v2))<0)return Length(v2);
    else if(dcmp(Dot(v1,v3))>0)return Length(v3);
    else return fabs(Cross(v1,v2)/Length(v1));
}
Point GetLineProjection(Point P,Point A,Point B)//点在直线上的投影
{
    Vector v=B-A;
    return A+v*(Dot(v,P-A)/Dot(v,v));
}
bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2)//判断线段相交,不在端点相交
{
    double c1=Cross(a2-a1,b1-a1),c2=Cross(a2-a1,b2-a1),c3=Cross(b2-b1,a1-b1),c4=Cross(b2-b1,a2-b1);
    return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0;
}
bool OnSegment(Point p,Point a1,Point a2)//判断点是否在线段上(不包括端点)
{
    return dcmp(Cross(a1-p,a2-p))==0&&dcmp(Dot(a1-p,a2-p))<0;
}

//多边型
double ConvexPolygonArea(Point* p,int n)//多边形面积,,点按顺序
{
    double area=0;
    for(int i=1;i<n-1;i++)
        area+=Cross(p[i]-p[0],p[i+1]-p[0]);
    return area/2;
}
int ConvexHull(Point *p,Point *ch,int n)//求凸包
{
    sort(p,p+n);
    int i,m=0,k;
    for(i=0;i<n;i++)
    {
        while(m>1&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)m--;
        ch[m++]=p[i];
    }
    k=m;
    for(i=n-2;i>=0;i--)
    {
        while(m>k&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)m--;
        ch[m++]=p[i];
    }
    if(n>1)m--;
    return m;
}

const int maxn=1e4+10;
Point p[maxn],ch[maxn];
int n;
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        int i,j,k,m;
        for(i=0;i<n;i++)
            scanf("%lf%lf",&p[i].x,&p[i].y);
        m=ConvexHull(p,ch,n);
        double ans;
        ans=ConvexPolygonArea(ch,m);
        printf("%d\n",(int)(ans/50.0));
    }
    return 0;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: