zoukankan      html  css  js  c++  java
  • poj1228 Grandpa's Estate

    地址:http://poj.org/problem?id=1228

    题目:

    Grandpa's Estate
    Time Limit: 1000MS   Memory Limit: 10000K
    Total Submissions: 14326   Accepted: 4004

    Description

    Being the only living descendant of his grandfather, Kamran the Believer inherited all of the grandpa's belongings. The most valuable one was a piece of convex polygon shaped farm in the grandpa's birth village. The farm was originally separated from the neighboring farms by a thick rope hooked to some spikes (big nails) placed on the boundary of the polygon. But, when Kamran went to visit his farm, he noticed that the rope and some spikes are missing. Your task is to write a program to help Kamran decide whether the boundary of his farm can be exactly determined only by the remaining spikes.

    Input

    The first line of the input file contains a single integer t (1 <= t <= 10), the number of test cases, followed by the input data for each test case. The first line of each test case contains an integer n (1 <= n <= 1000) which is the number of remaining spikes. Next, there are n lines, one line per spike, each containing a pair of integers which are x and y coordinates of the spike.

    Output

    There should be one output line per test case containing YES or NO depending on whether the boundary of the farm can be uniquely determined from the input.

    Sample Input

    1
    6 
    0 0
    1 2
    3 4
    2 0
    2 4 
    5 0
    

    Sample Output

    NO

    Source

     
    思路:
      题意好迷,让你判断凸包是否是稳定凸包。
      稳定凸包:任意一条边上至少有3个点。
      
      我感觉的板子好啰嗦,我要炸了,我要重写板子!!!
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    
    
    using namespace std;
    const double PI = acos(-1.0);
    const double eps = 1e-10;
    //
    class Point
    {
    public:
        double x, y;
    
        Point(){}
        Point(double x, double y):x(x),y(y){}
    
        bool operator < (const Point &_se) const
        {
            return x<_se.x || (x==_se.x && y<_se.y);
        }
        /*******判断ta与tb的大小关系*******/
        static int sgn(double ta,double tb)
        {
            if(fabs(ta-tb)<eps)return 0;
            if(ta<tb)   return -1;
            return 1;
        }
        static double xmult(const Point &po, const Point &ps, const Point &pe)
        {
            return (ps.x - po.x) * (pe.y - po.y) - (pe.x - po.x) * (ps.y - po.y);
        }
        friend Point operator + (const Point &_st,const Point &_se)
        {
            return Point(_st.x + _se.x, _st.y + _se.y);
        }
        friend Point operator - (const Point &_st,const Point &_se)
        {
            return Point(_st.x - _se.x, _st.y - _se.y);
        }
        //点位置相同(double类型)
        bool operator == (const Point &_off) const
        {
            return  Point::sgn(x, _off.x) == 0 && Point::sgn(y, _off.y) == 0;
        }
        //点位置不同(double类型)
        bool operator != (const Point &_Off) const
        {
            return ((*this) == _Off) == false;
        }
        //两点间距离的平方
        static double dis2(const Point &_st,const Point &_se)
        {
            return (_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y);
        }
        //两点间距离
        static double dis(const Point &_st, const Point &_se)
        {
            return sqrt((_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y));
        }
    };
    //两点表示的向量
    class Line
    {
    public:
        Point s, e;//两点表示,起点[s],终点[e]
        double a, b, c;//一般式,ax+by+c=0
        double angle;//向量的角度,[-pi,pi]
        Line(){}
        Line(const Point &s, const Point &e):s(s),e(e){get_angle(1);}
        Line(double _a,double _b,double _c):a(_a),b(_b),c(_c){}
    
        //向量与点的叉乘,参数:点[_Off]
        //[点相对向量位置判断]
        double operator /(const Point &_Off) const
        {
            return (_Off.y - s.y) * (e.x - s.x) - (_Off.x - s.x) * (e.y - s.y);
        }
        //向量与向量的叉乘,参数:向量[_Off]
        friend double operator /(const Line &_st,const Line &_se)
        {
            return (_st.e.x - _st.s.x) * (_se.e.y - _se.s.y) - (_st.e.y - _st.s.y) * (_se.e.x - _se.s.x);
        }
        friend double operator *(const Line &_st,const Line &_se)
        {
            return (_st.e.x - _st.s.x) * (_se.e.x - _se.s.x) - (_st.e.y - _st.s.y) * (_se.e.y - _se.s.y);
        }
        //从两点表示转换为一般表示
        //a=y2-y1,b=x1-x2,c=x2*y1-x1*y2
        bool pton()
        {
            a = e.y - s.y;
            b = s.x - e.x;
            c = e.x * s.y - e.y * s.x;
            return true;
        }
        //求直线或向量的角度
        double get_angle(bool isVector)
        {
            angle=atan2(e.y-s.y,e.x-s.x);
            if(!isVector && angle<0)
                angle+=PI;
            return angle;
        }
    
        //
        bool operator < (const Line &ta)const
        {
            return angle<ta.angle;
        }
        //-----------点和直线(向量)-----------
        //点在向量左边(右边的小于号改成大于号即可,在对应直线上则加上=号)
        //参数:点[_Off],向量[_Ori]
        friend bool operator<(const Point &_Off, const Line &_Ori)
        {
            return (_Ori.e.y - _Ori.s.y) * (_Off.x - _Ori.s.x)
                < (_Off.y - _Ori.s.y) * (_Ori.e.x - _Ori.s.x);
        }
    
        //点在直线上,参数:点[_Off]
        bool lhas(const Point &_Off) const
        {
            return Point::sgn((*this) / _Off, 0) == 0;
        }
        //点在线段上,参数:点[_Off]
        bool shas(const Point &_Off) const
        {
            return lhas(_Off)
                && Point::sgn(_Off.x - min(s.x, e.x), 0) > 0 && Point::sgn(_Off.x - max(s.x, e.x), 0) < 0
                && Point::sgn(_Off.y - min(s.y, e.y), 0) > 0 && Point::sgn(_Off.y - max(s.y, e.y), 0) < 0;
        }
    
        //点到直线/线段的距离
        //参数: 点[_Off], 是否是线段[isSegment](默认为直线)
        double dis(const Point &_Off, bool isSegment = false)
        {
            ///化为一般式
            pton();
    
            //到直线垂足的距离
            double td = (a * _Off.x + b * _Off.y + c) / sqrt(a * a + b * b);
    
            //如果是线段判断垂足
            if(isSegment)
            {
                double xp = (b * b * _Off.x - a * b * _Off.y - a * c) / ( a * a + b * b);
                double yp = (-a * b * _Off.x + a * a * _Off.y - b * c) / (a * a + b * b);
                double xb = max(s.x, e.x);
                double yb = max(s.y, e.y);
                double xs = s.x + e.x - xb;
                double ys = s.y + e.y - yb;
                if(xp > xb + eps || xp < xs - eps || yp > yb + eps || yp < ys - eps)
                    td = min(Point::dis(_Off,s), Point::dis(_Off,e));
            }
    
            return fabs(td);
        }
    
        //关于直线对称的点
        Point mirror(const Point &_Off) const
        {
            ///注意先转为一般式
            Point ret;
            double d = a * a + b * b;
            ret.x = (b * b * _Off.x - a * a * _Off.x - 2 * a * b * _Off.y - 2 * a * c) / d;
            ret.y = (a * a * _Off.y - b * b * _Off.y - 2 * a * b * _Off.x - 2 * b * c) / d;
            return ret;
        }
        //计算两点的中垂线
        static Line ppline(const Point &_a, const Point &_b)
        {
            Line ret;
            ret.s.x = (_a.x + _b.x) / 2;
            ret.s.y = (_a.y + _b.y) / 2;
            //一般式
            ret.a = _b.x - _a.x;
            ret.b = _b.y - _a.y;
            ret.c = (_a.y - _b.y) * ret.s.y + (_a.x - _b.x) * ret.s.x;
            //两点式
            if(std::fabs(ret.a) > eps)
            {
                ret.e.y = 0.0;
                ret.e.x = - ret.c / ret.a;
                if(ret.e == ret. s)
                {
                    ret.e.y = 1e10;
                    ret.e.x = - (ret.c - ret.b * ret.e.y) / ret.a;
                }
            }
            else
            {
                ret.e.x = 0.0;
                ret.e.y = - ret.c / ret.b;
                if(ret.e == ret. s)
                {
                    ret.e.x = 1e10;
                    ret.e.y = - (ret.c - ret.a * ret.e.x) / ret.b;
                }
            }
            return ret;
        }
    
        //------------直线和直线(向量)-------------
        //直线向左边平移t的距离
        Line& moveLine(double t)
        {
            Point of;
            of=Point(-(e.y-s.y),e.x-s.x);
            double dis=sqrt(of.x*of.x+of.y*of.y);
            of.x=of.x*t/dis,of.y=of.y*t/dis;
            s=s+of,e=e+of;
            return *this;
        }
        //直线重合,参数:直线向量[_st],[_se]
        static bool equal(const Line &_st, const Line &_se)
        {
            return _st.lhas(_se.e) && _se.lhas(_se.s);
        }
        //直线平行,参数:直线向量[_st],[_se]
        static bool parallel(const Line &_st,const Line &_se)
        {
            return Point::sgn(_st / _se, 0) == 0;
        }
        //两直线(线段)交点,参数:直线向量[_st],[_se],交点
        //返回-1代表平行,0代表重合,1代表相交
        static bool crossLPt(const Line &_st,const Line &_se,Point &ret)
        {
            if(Line::parallel(_st,_se))
            {
                if(Line::equal(_st,_se)) return 0;
                return -1;
            }
            ret = _st.s;
            double t = (Line(_st.s,_se.s)/_se)/(_st/_se);
            ret.x += (_st.e.x - _st.s.x) * t;
            ret.y += (_st.e.y - _st.s.y) * t;
            return 1;
        }
        //------------线段和直线(向量)----------
        //线段和直线交
        //参数:直线[_st],线段[_se]
        friend bool crossSL(const Line &_st,const Line &_se)
        {
            return Point::sgn((_st / _se.s) * (_st / _se.e) ,0) <= 0;
        }
    
        //------------线段和线段(向量)----------
        //判断线段是否相交(注意添加eps),参数:线段[_st],线段[_se]
        static bool isCrossSS(const Line &_st,const Line &_se)
        {
            //1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交
            //2.跨立试验(等于0时端点重合)
            return
                max(_st.s.x, _st.e.x) >= min(_se.s.x, _se.e.x) &&
                max(_se.s.x, _se.e.x) >= min(_st.s.x, _st.e.x) &&
                max(_st.s.y, _st.e.y) >= min(_se.s.y, _se.e.y) &&
                max(_se.s.y, _se.e.y) >= min(_st.s.y, _st.e.y) &&
                Point::sgn((_st / Line(_st.s, _se.s)) * (_st / Line(_st.s, _se.e)), 0) <= 0 &&
                Point::sgn((_se / Line(_se.s, _st.s)) * (_se / Line(_se.s, _st.e)), 0) <= 0;
        }
    };
    Point ptsort;
    bool gcmp(const Point &ta,const Point &tb)/// 选取与最后一条确定边夹角最小的点,即余弦值最大者
    {
        double tmp=Point::xmult(ptsort,ta,tb);
        if(Point::sgn(tmp,0)==0)
            return Point::dis(ptsort,ta)<Point::dis(ptsort,tb);
        else if(tmp>0)
            return 1;
        return 0;
    }
    class Polygon
    {
    public:
        const static int maxpn = 5e4+7;
        Point pt[maxpn];//点(顺时针或逆时针)
        int n;//点的个数
    
    
        //求多边形面积,多边形内点必须顺时针或逆时针
        double area() const
        {
            double ans = 0.0;
            for(int i = 0; i < n; i ++)
            {
                int nt = (i + 1) % n;
                ans += pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
            }
            return fabs(ans / 2.0);
        }
        //求多边形重心,多边形内点必须顺时针或逆时针
        Point gravity() const
        {
            Point ans;
            ans.x = ans.y = 0.0;
            double area = 0.0;
            for(int i = 0; i < n; i ++)
            {
                int nt = (i + 1) % n;
                double tp = pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
                area += tp;
                ans.x += tp * (pt[i].x + pt[nt].x);
                ans.y += tp * (pt[i].y + pt[nt].y);
            }
            ans.x /= 3 * area;
            ans.y /= 3 * area;
            return ans;
        }
        //判断点在凸多边形内,参数:点[_Off]
        bool chas(const Point &_Off) const
        {
            double tp = 0, np;
            for(int i = 0; i < n; i ++)
            {
                np = Line(pt[i], pt[(i + 1) % n]) / _Off;
                if(tp * np < -eps)
                    return false;
                tp = (fabs(np) > eps)?np: tp;
            }
            return true;
        }
    
    
        /** 卷包裹法求点集凸包,_p为输入点集,_n为点的数量 **/
        void ConvexClosure(Point _p[],int _n)
        {
            sort(_p,_p+_n);
            n=0;
            for(int i=0;i<_n;i++)
            {
                while(n>1&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<0)
                    n--;
                pt[n++]=_p[i];
            }
            int _key=n;
            for(int i=_n-2;i>=0;i--)
            {
                while(n>_key&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<0)
                    n--;
                pt[n++]=_p[i];
            }
            if(n>1)   n--;//除去重复的点,该点已是凸包凸包起点
        }
        /****** 寻找凸包的graham 扫描法********************/
        /****** _p为输入的点集,_n为点的数量****************/
        /**使用时需把gmp函数放在Polygon类上面L,ine类下面,并且看情况修改pt[0]**/
    
        void graham(Point _p[],int _n)
        {
            int cur=0;
            for(int i=1;i<_n;i++)
                if(_p[cur].y>_p[i].y || (Point::sgn(_p[cur].y,_p[i].y)==0 && _p[cur].x>_p[i].x))
                    cur=i;
            swap(_p[cur],_p[0]);
            n=0,pt[n++]=_p[0],ptsort=_p[0];
            if(_n==1)   return;
            sort(_p+1,_p+_n,gcmp);
            pt[n++]=_p[1],pt[n++]=_p[2];
            for(int i=3;i<_n;i++)
            {
                while(n>1 && Point::sgn(Point::xmult(pt[n-2],pt[n-1],_p[i]),0)<0)// 当凸包退化成直线时需特别注意n
                    n--;
                pt[n++]=_p[i];
            }
        }
    
    
    };
    
    Point pt[1200];
    Polygon py;
    Line ln;
    int main(void)
    {
        int t,n,cs=1;cin>>t;
        while(t--)
        {
            int ff=1;
            scanf("%d",&n);
            for(int i=0;i<n;i++)
                scanf("%lf%lf",&pt[i].x,&pt[i].y);
            if(n<6)  puts("NO");
            else
            {
                py.graham(pt,n);
                py.pt[py.n]=py.pt[0];
        //        for(int i=0;i<py.n;i++)
        //            printf("==%.2f %.2f
    ",py.pt[i].x,py.pt[i].y);
                for(int i=1;i<py.n-1&&ff;i++)
                if(Point::xmult(py.pt[i-1],py.pt[i+1],py.pt[i])!=0&&Point::xmult(py.pt[i],py.pt[i+2],py.pt[i+1])!=0)
                    ff=0;
                if(ff)
                    puts("YES");
                else
                    puts("NO");
            }
    
        }
        return 0;
    }
  • 相关阅读:
    Express 框架以及与http-proxy-middleware整合实现代理
    Ajax跨域问题
    XStream处理XML用法
    vue中引入mintui、vux重构简单的APP项目
    vue-cli安装以及创建一个简单的项目(二)(vuex使用、发行一个简单的app)
    JS实现图片懒加载
    ECMAScript 6.0 简要学习
    【Android】自定义控件让TextView的drawableLeft与文本一起居中显示
    【Android】不使用WebView来执行Javascript脚本(Rhino)
    【Android】Fragment真正意义上的onResume和onPause
  • 原文地址:https://www.cnblogs.com/weeping/p/7625794.html
Copyright © 2011-2022 走看看