• 三道半平面交测模板题 Poj1474 Poj 3335 Poj 3130


    求半平面交的算法是zzy大神的排序增量法。

      1 ///Poj 1474
      2 #include <cmath>
      3 #include <algorithm>
      4 #include <cstdio>
      5 using namespace std;
      6 const double eps = 1e-10;
      7 //
      8 class Point
      9 {
     10 public:
     11     double x, y;
     12 
     13     Point(){}
     14     Point(double x, double y):x(x),y(y){}
     15 
     16     bool operator < (const Point &_se) const
     17     {
     18         return x<_se.x || (x==_se.x && y<_se.y);
     19     }
     20     /*******判断ta与tb的大小关系*******/
     21     static int sgn(double ta,double tb)
     22     {
     23         if(fabs(ta-tb)<eps)return 0;
     24         if(ta<tb)   return -1;
     25         return 1;
     26     }
     27     static double xmult(const Point &ps, const Point &pe, const Point &po)
     28     {
     29         return (ps.x - po.x) * (pe.y - po.y) - (pe.x - po.x) * (ps.y - po.y);
     30     }
     31     friend Point operator + (const Point &_st,const Point &_se)
     32     {
     33         return Point(_st.x + _se.x, _st.y + _se.y);
     34     }
     35     friend Point operator - (const Point &_st,const Point &_se)
     36     {
     37         return Point(_st.x - _se.x, _st.y - _se.y);
     38     }
     39     //点位置相同(double类型)
     40     bool operator == (const Point &_off) const
     41     {
     42         return  Point::sgn(x, _off.x) == 0 && Point::sgn(y, _off.y) == 0;
     43     }
     44     //点位置不同(double类型)
     45     bool operator != (const Point &_Off) const
     46     {
     47         return ((*this) == _Off) == false;
     48     }
     49     //两点间距离的平方
     50     static double dis2(const Point &_st,const Point &_se)
     51     {
     52         return (_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y);
     53     }
     54     //两点间距离
     55     static double dis(const Point &_st, const Point &_se)
     56     {
     57         return sqrt((_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y));
     58     }
     59     static double fArea(const Point &_sa,const Point &_sb,const Point &_sc)
     60     {
     61         return fabs(Point::xmult(_sa,_sb,_sc))/2;
     62     }
     63 };
     64 //两点表示的向量
     65 class Line
     66 {
     67 public:
     68     Point s, e;//两点表示,起点[s],终点[e]
     69     double a, b, c;//一般式,ax+by+c=0
     70 
     71     Line(){}
     72     Line(const Point &s, const Point &e):s(s),e(e){}
     73     Line(double _a,double _b,double _c):a(_a),b(_b),c(_c){}
     74 
     75     //向量与点的叉乘,参数:点[_Off]
     76     //[点相对向量位置判断]
     77     double operator /(const Point &_Off) const
     78     {
     79         return (_Off.y - s.y) * (e.x - s.x) - (_Off.x - s.x) * (e.y - s.y);
     80     }
     81     //向量与向量的叉乘,参数:向量[_Off]
     82     friend double operator /(const Line &_st,const Line &_se)
     83     {
     84         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);
     85     }
     86     friend double operator *(const Line &_st,const Line &_se)
     87     {
     88         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);
     89     }
     90     //从两点表示转换为一般表示
     91     //a=y2-y1,b=x1-x2,c=x2*y1-x1*y2
     92     bool pton()
     93     {
     94         a = e.y - s.y;
     95         b = s.x - e.x;
     96         c = e.x * s.y - e.y * s.x;
     97         return true;
     98     }
     99 
    100     //-----------点和直线(向量)-----------
    101     //点在向量左边(右边的小于号改成大于号即可,在对应直线上则加上=号)
    102     //参数:点[_Off],向量[_Ori]
    103     friend bool operator<(const Point &_Off, const Line &_Ori)
    104     {
    105         return (_Ori.e.y - _Ori.s.y) * (_Off.x - _Ori.s.x)
    106             < (_Off.y - _Ori.s.y) * (_Ori.e.x - _Ori.s.x);
    107     }
    108 
    109     //点在直线上,参数:点[_Off]
    110     bool lhas(const Point &_Off) const
    111     {
    112         return Point::sgn((*this) / _Off, 0) == 0;
    113     }
    114     //点在线段上,参数:点[_Off]
    115     bool shas(const Point &_Off) const
    116     {
    117         return lhas(_Off)
    118             && Point::sgn(_Off.x - min(s.x, e.x), 0) > 0 && Point::sgn(_Off.x - max(s.x, e.x), 0) < 0
    119             && Point::sgn(_Off.y - min(s.y, e.y), 0) > 0 && Point::sgn(_Off.y - max(s.y, e.y), 0) < 0;
    120     }
    121 
    122     //点到直线/线段的距离
    123     //参数: 点[_Off], 是否是线段[isSegment](默认为直线)
    124     double dis(const Point &_Off, bool isSegment = false)
    125     {
    126         ///化为一般式
    127         pton();
    128 
    129         //到直线垂足的距离
    130         double td = (a * _Off.x + b * _Off.y + c) / sqrt(a * a + b * b);
    131 
    132         //如果是线段判断垂足
    133         if(isSegment)
    134         {
    135             double xp = (b * b * _Off.x - a * b * _Off.y - a * c) / ( a * a + b * b);
    136             double yp = (-a * b * _Off.x + a * a * _Off.y - b * c) / (a * a + b * b);
    137             double xb = max(s.x, e.x);
    138             double yb = max(s.y, e.y);
    139             double xs = s.x + e.x - xb;
    140             double ys = s.y + e.y - yb;
    141             if(xp > xb + eps || xp < xs - eps || yp > yb + eps || yp < ys - eps)
    142                 td = min(Point::dis(_Off,s), Point::dis(_Off,e));
    143         }
    144 
    145         return fabs(td);
    146     }
    147 
    148     //关于直线对称的点
    149     Point mirror(const Point &_Off) const
    150     {
    151         ///注意先转为一般式
    152         Point ret;
    153         double d = a * a + b * b;
    154         ret.x = (b * b * _Off.x - a * a * _Off.x - 2 * a * b * _Off.y - 2 * a * c) / d;
    155         ret.y = (a * a * _Off.y - b * b * _Off.y - 2 * a * b * _Off.x - 2 * b * c) / d;
    156         return ret;
    157     }
    158     //计算两点的中垂线
    159     static Line ppline(const Point &_a, const Point &_b)
    160     {
    161         Line ret;
    162         ret.s.x = (_a.x + _b.x) / 2;
    163         ret.s.y = (_a.y + _b.y) / 2;
    164         //一般式
    165         ret.a = _b.x - _a.x;
    166         ret.b = _b.y - _a.y;
    167         ret.c = (_a.y - _b.y) * ret.s.y + (_a.x - _b.x) * ret.s.x;
    168         //两点式
    169         if(fabs(ret.a) > eps)
    170         {
    171             ret.e.y = 0.0;
    172             ret.e.x = - ret.c / ret.a;
    173             if(ret.e == ret. s)
    174             {
    175                 ret.e.y = 1e10;
    176                 ret.e.x = - (ret.c - ret.b * ret.e.y) / ret.a;
    177             }
    178         }
    179         else
    180         {
    181             ret.e.x = 0.0;
    182             ret.e.y = - ret.c / ret.b;
    183             if(ret.e == ret. s)
    184             {
    185                 ret.e.x = 1e10;
    186                 ret.e.y = - (ret.c - ret.a * ret.e.x) / ret.b;
    187             }
    188         }
    189         return ret;
    190     }
    191 
    192     //------------直线和直线(向量)-------------
    193     //直线重合,参数:直线向量[_st],[_se]
    194     static bool equal(const Line &_st, const Line &_se)
    195     {
    196         return _st.lhas(_se.e) && _se.lhas(_se.s);
    197     }
    198     //直线平行,参数:直线向量[_st],[_se]
    199     static bool parallel(const Line &_st,const Line &_se)
    200     {
    201         return Point::sgn(_st / _se, 0) == 0;
    202     }
    203     //两直线(线段)交点,参数:直线向量[_st],[_se],交点
    204     //返回-1代表平行,0代表重合,1代表相交
    205     static bool crossLPt(const Line &_st,const Line &_se,Point &ret)
    206     {
    207         if(parallel(_st,_se))
    208         {
    209             if(Line::equal(_st,_se)) return 0;
    210             return -1;
    211         }
    212         ret = _st.s;
    213         double t = (Line(_st.s,_se.s)/_se)/(_st/_se);
    214         ret.x += (_st.e.x - _st.s.x) * t;
    215         ret.y += (_st.e.y - _st.s.y) * t;
    216         return 1;
    217     }
    218     //------------线段和直线(向量)----------
    219     //线段和直线交
    220     //参数:直线[_st],线段[_se]
    221     friend bool crossSL(const Line &_st,const Line &_se)
    222     {
    223         return Point::sgn((_st / _se.s) * (_st / _se.e) ,0) <= 0;
    224     }
    225 
    226     //------------线段和线段(向量)----------
    227     //判断线段是否相交(注意添加eps),参数:线段[_st],线段[_se]
    228     static bool isCrossSS(const Line &_st,const Line &_se)
    229     {
    230         //1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交
    231         //2.跨立试验(等于0时端点重合)
    232         return
    233             max(_st.s.x, _st.e.x) >= min(_se.s.x, _se.e.x) &&
    234             max(_se.s.x, _se.e.x) >= min(_st.s.x, _st.e.x) &&
    235             max(_st.s.y, _st.e.y) >= min(_se.s.y, _se.e.y) &&
    236             max(_se.s.y, _se.e.y) >= min(_st.s.y, _st.e.y) &&
    237             Point::sgn((_st / Line(_st.s, _se.s)) * (_st / Line(_st.s, _se.e)), 0) <= 0 &&
    238             Point::sgn((_se / Line(_se.s, _st.s)) * (_se / Line(_se.s, _st.e)), 0) <= 0;
    239     }
    240 };
    241 class Polygon
    242 {
    243 public:
    244     const static int maxpn = 500;
    245     Point pt[maxpn];//点(顺时针或逆时针)
    246     int n;//点的个数
    247 
    248     Point& operator[](int _p)
    249     {
    250         return pt[_p];
    251     }
    252 
    253     //求多边形面积,多边形内点必须顺时针或逆时针
    254     double area() const
    255     {
    256         double ans = 0.0;
    257         for(int i = 0; i < n; i ++)
    258         {
    259             int nt = (i + 1) % n;
    260             ans += pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
    261         }
    262         return fabs(ans / 2.0);
    263     }
    264     //求多边形重心,多边形内点必须顺时针或逆时针
    265     Point gravity() const
    266     {
    267         Point ans;
    268         ans.x = ans.y = 0.0;
    269         double area = 0.0;
    270         for(int i = 0; i < n; i ++)
    271         {
    272             int nt = (i + 1) % n;
    273             double tp = pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
    274             area += tp;
    275             ans.x += tp * (pt[i].x + pt[nt].x);
    276             ans.y += tp * (pt[i].y + pt[nt].y);
    277         }
    278         ans.x /= 3 * area;
    279         ans.y /= 3 * area;
    280         return ans;
    281     }
    282     //判断点在凸多边形内,参数:点[_Off]
    283     bool chas(const Point &_Off) const
    284     {
    285         double tp = 0, np;
    286         for(int i = 0; i < n; i ++)
    287         {
    288             np = Line(pt[i], pt[(i + 1) % n]) / _Off;
    289             if(tp * np < -eps)
    290                 return false;
    291             tp = (fabs(np) > eps)?np: tp;
    292         }
    293         return true;
    294     }
    295     //判断点是否在任意多边形内[射线法],O(n)
    296     bool ahas(const Point &_Off) const
    297     {
    298         int ret = 0;
    299         double infv = 1e-10;//坐标系最大范围
    300         Line l = Line(_Off, Point( -infv ,_Off.y));
    301         for(int i = 0; i < n; i ++)
    302         {
    303             Line ln = Line(pt[i], pt[(i + 1) % n]);
    304             if(fabs(ln.s.y - ln.e.y) > eps)
    305             {
    306                 Point tp = (ln.s.y > ln.e.y)? ln.s: ln.e;
    307                 if(fabs(tp.y - _Off.y) < eps && tp.x < _Off.x + eps)
    308                     ret ++;
    309             }
    310             else if(Line::isCrossSS(ln,l))
    311                 ret ++;
    312         }
    313         return (ret % 2 == 1);
    314     }
    315     //凸多边形被直线分割,参数:直线[_Off]
    316     Polygon split(Line _Off)
    317     {
    318         //注意确保多边形能被分割
    319         Polygon ret;
    320         Point spt[2];
    321         double tp = 0.0, np;
    322         bool flag = true;
    323         int i, pn = 0, spn = 0;
    324         for(i = 0; i < n; i ++)
    325         {
    326             if(flag)
    327                 pt[pn ++] = pt[i];
    328             else
    329                 ret.pt[ret.n ++] = pt[i];
    330             np = _Off / pt[(i + 1) % n];
    331             if(tp * np < -eps)
    332             {
    333                 flag = !flag;
    334                 Line::crossLPt(_Off,Line(pt[i], pt[(i + 1) % n]),spt[spn++]);
    335             }
    336             tp = (std::fabs(np) > eps)?np: tp;
    337         }
    338         ret.pt[ret.n ++] = spt[0];
    339         ret.pt[ret.n ++] = spt[1];
    340         n = pn;
    341         return ret;
    342     }
    343 
    344 
    345     /** 卷包裹法求点集凸包,_p为输入点集,_n为点的数量 **/
    346     void ConvexClosure(Point _p[],int _n)
    347     {
    348         sort(_p,_p+_n);
    349         n=0;
    350         for(int i=0;i<_n;i++)
    351         {
    352             while(n>1&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<=0)
    353                 n--;
    354             pt[n++]=_p[i];
    355         }
    356         int _key=n;
    357         for(int i=_n-2;i>=0;i--)
    358         {
    359             while(n>_key&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<=0)
    360                 n--;
    361             pt[n++]=_p[i];
    362         }
    363         if(n>1)   n--;//除去重复的点,该点已是凸包凸包起点
    364     }
    365 //    /****** 寻找凸包的graham 扫描法********************/
    366 //    /****** _p为输入的点集,_n为点的数量****************/
    367 //    /**使用时需把gmp函数放在类外,并且看情况修改pt[0]**/
    368 //    bool gcmp(const Point &ta,const Point &tb)/// 选取与最后一条确定边夹角最小的点,即余弦值最大者
    369 //    {
    370 //        double tmp=Line(pt[0],ta)/Line(pt[0],tb);
    371 //        if(Point::sgn(tmp,0)==0)
    372 //            return Point::dis(pt[0],ta)<Point::dis(pt[0],tb);
    373 //        else if(tmp>0)
    374 //            return 1;
    375 //        return 0;
    376 //    }
    377 //    void graham(Point _p[],int _n)
    378 //    {
    379 //        int cur=0;
    380 //        for(int i=1;i<_n;i++)
    381 //            if(Point::sgn(_p[cur].y,_p[i].y)>0 || (Point::sgn(_p[cur].y,_p[i].y)==0 && Point::sgn(_p[cur].x,_p[i].x)>0))
    382 //                cur=i;
    383 //        swap(_p[cur],_p[0]);
    384 //        n=0,pt[n++]=_p[0];
    385 //        if(_n==1)   return;
    386 //        sort(_p+1,_p+_n,Polygon::gcmp);
    387 //        pt[n++]=_p[1],pt[n++]=_p[2];
    388 //        for(int i=3;i<_n;i++)
    389 //        {
    390 //            while(Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<0)
    391 //                n--;
    392 //            pt[n++]=_p[i];
    393 //        }
    394 //    }
    395     //凸包旋转卡壳(注意点必须顺时针或逆时针排列)
    396     //返回值凸包直径的平方(最远两点距离的平方)
    397     double rotating_calipers()
    398     {
    399         int i = 1;
    400         double ret = 0.0;
    401         pt[n] = pt[0];
    402         for(int j = 0; j < n; j ++)
    403         {
    404             while(fabs(Point::xmult(pt[j], pt[j + 1], pt[i + 1])) > fabs(Point::xmult(pt[j], pt[j + 1], pt[i])) + eps)
    405                 i = (i + 1) % n;
    406             //pt[i]和pt[j],pt[i + 1]和pt[j + 1]可能是对踵点
    407             ret = max(ret, max(Point::dis(pt[i],pt[j]), Point::dis(pt[i + 1],pt[j + 1])));
    408         }
    409         return ret;
    410     }
    411 
    412     //凸包旋转卡壳(注意点必须逆时针排列)
    413     //返回值两凸包的最短距离
    414     double rotating_calipers(Polygon &_Off)
    415     {
    416         int i = 0;
    417         double ret = 1e10;//inf
    418         pt[n] = pt[0];
    419         _Off.pt[_Off.n] = _Off.pt[0];
    420         //注意凸包必须逆时针排列且pt[0]是左下角点的位置
    421         while(_Off.pt[i + 1].y > _Off.pt[i].y)
    422             i = (i + 1) % _Off.n;
    423         for(int j = 0; j < n; j ++)
    424         {
    425             double tp;
    426             //逆时针时为 >,顺时针则相反
    427             while((tp = Point::xmult(pt[j], pt[j + 1], _Off.pt[i + 1]) - Point::xmult( pt[j], pt[j + 1], _Off.pt[i])) > eps)
    428                 i = (i + 1) % _Off.n;
    429             //(pt[i],pt[i+1])和(_Off.pt[j],_Off.pt[j + 1])可能是最近线段
    430             ret = min(ret, Line(pt[j], pt[j + 1]).dis(_Off.pt[i], true));
    431             ret = min(ret, Line(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j + 1], true));
    432             if(tp > -eps)//如果不考虑TLE问题最好不要加这个判断
    433             {
    434                 ret = min(ret, Line(pt[j], pt[j + 1]).dis(_Off.pt[i + 1], true));
    435                 ret = min(ret, Line(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j], true));
    436             }
    437         }
    438         return ret;
    439     }
    440 
    441     //-----------半平面交-------------
    442     //复杂度:O(nlog2(n))
    443     //#include <algorithm>
    444     //半平面计算极角函数[如果考虑效率可以用成员变量记录]
    445     static double hpc_pa(const Line &_Off)
    446     {
    447         return atan2(_Off.e.y - _Off.s.y, _Off.e.x - _Off.s.x);
    448     }
    449     //半平面交排序函数[优先顺序: 1.极角 2.前面的直线在后面的左边]
    450     static bool hpc_cmp(const Line &l, const Line &r)
    451     {
    452         double lp = hpc_pa(l), rp = hpc_pa(r);
    453         if(fabs(lp - rp) > eps)
    454             return lp < rp;
    455         return Point::xmult(l.s, r.e, r.s) < -eps;
    456     }
    457     static int judege(const Line &_lx,const Line &_ly,const Line &_lz)
    458     {
    459         Point tmp;
    460         Line::crossLPt(_lx,_ly,tmp);
    461         return Point::sgn(Point::xmult(tmp,_lz.e,_lz.s),0);
    462     }
    463     //获取半平面交的多边形(多边形的核)
    464     //参数:向量集合[l],向量数量[ln];(半平面方向在向量左边)
    465     //函数运行后如果n[即返回多边形的点数量]为0则不存在半平面交的多边形(不存在区域或区域面积无穷大)
    466     Polygon& halfPanelCross(Line _Off[], int ln)
    467     {
    468         Line dequeue[maxpn];//用于计算的双端队列
    469         int i, tn;
    470         sort(_Off, _Off + ln, hpc_cmp);
    471         //平面在向量左边的筛选
    472         for(i = tn = 1; i < ln; i ++)
    473             if(fabs(hpc_pa(_Off[i]) - hpc_pa(_Off[i - 1])) > eps)
    474                 _Off[tn ++] = _Off[i];
    475         ln = tn,n = 0;
    476         int bot = 0, top = 1;
    477         dequeue[0] = _Off[0];
    478         dequeue[1] = _Off[1];
    479         //for(int i=0;i<ln;i++)
    480         //   printf("%.2f %.2f %.2f %.2f
    ",_Off[i].s.x,_Off[i].s.y,_Off[i].e.x,_Off[i].e.y);
    481         for(i = 2; i < ln; i ++)
    482         {
    483             while(bot < top &&  judege(dequeue[top],dequeue[top-1],_Off[i]) > 0)
    484                 top --;
    485             while(bot < top &&  judege(dequeue[bot],dequeue[bot+1],_Off[i]) > 0)
    486                 bot ++;
    487             dequeue[++ top] = _Off[i];
    488         }
    489         while(bot < top && judege(dequeue[top],dequeue[top-1],dequeue[bot]) > 0)
    490             top --;
    491         while(bot < top && judege(dequeue[bot],dequeue[bot+1],dequeue[top]) > 0)
    492             bot ++;
    493         //计算交点(注意不同直线形成的交点可能重合)
    494         if(top <= bot + 1)
    495             return (*this);
    496         for(i = bot; i < top; i ++)
    497             Line::crossLPt(dequeue[i],dequeue[i + 1],pt[n++]);
    498         if(bot < top + 1)
    499             Line::crossLPt(dequeue[bot],dequeue[top],pt[n++]);
    500         return (*this);
    501     }
    502 };
    503 
    504 int n;
    505 Point pt[200];
    506 Line ln[200];
    507 Polygon ans;
    508 
    509 int main(void)
    510 {
    511     //freopen("out.txt","w",stdout);
    512     int cse=1;
    513     while(scanf("%d",&n)==1&&n)
    514     {
    515         for(int i=0;i<n;i++)
    516             scanf("%lf%lf",&pt[i].x,&pt[i].y);
    517         pt[n++]=pt[0];
    518         for(int i=n-1;i;i--)
    519         {
    520             ln[i-1]=Line(pt[i],pt[i-1]);
    521             //printf("%.2f %.2f %.2f %.2f
    ",ln[i-1].s.x,ln[i-1].s.y,ln[i-1].e.x,ln[i-1].e.y);
    522         }
    523         ans.halfPanelCross(ln,n-1);
    524         printf("Floor #%d
    ",cse++);
    525         //printf("====%d
    ",ans.n);
    526         //for(int i=0;i<ans.n;i++)
    527         //printf("%.2f %.2f
    ",ans[i].x,ans[i].y);
    528         if(ans.n)
    529             printf("Surveillance is possible.
    
    ");
    530         else
    531             printf("Surveillance is impossible.
    
    ");
    532     }
    533 
    534     return 0;
    535 }
    View Code
      1 ///Poj 3335
      2 #include <iostream>
      3 #include <cstdio>
      4 #include <algorithm>
      5 #include <cmath>
      6 using namespace std;
      7 const double eps = 1e-10;
      8 //
      9 class Point
     10 {
     11 public:
     12     double x, y;
     13 
     14     Point(){}
     15     Point(double x, double y):x(x),y(y){}
     16 
     17     bool operator < (const Point &_se) const
     18     {
     19         return x<_se.x || (x==_se.x && y<_se.y);
     20     }
     21     /*******判断ta与tb的大小关系*******/
     22     static int sgn(double ta,double tb)
     23     {
     24         if(fabs(ta-tb)<eps)return 0;
     25         if(ta<tb)   return -1;
     26         return 1;
     27     }
     28     static double xmult(const Point &ps, const Point &pe, const Point &po)
     29     {
     30         return (ps.x - po.x) * (pe.y - po.y) - (pe.x - po.x) * (ps.y - po.y);
     31     }
     32     friend Point operator + (const Point &_st,const Point &_se)
     33     {
     34         return Point(_st.x + _se.x, _st.y + _se.y);
     35     }
     36     friend Point operator - (const Point &_st,const Point &_se)
     37     {
     38         return Point(_st.x - _se.x, _st.y - _se.y);
     39     }
     40     //点位置相同(double类型)
     41     bool operator == (const Point &_off) const
     42     {
     43         return  Point::sgn(x, _off.x) == 0 && Point::sgn(y, _off.y) == 0;
     44     }
     45     //点位置不同(double类型)
     46     bool operator != (const Point &_Off) const
     47     {
     48         return ((*this) == _Off) == false;
     49     }
     50     //两点间距离的平方
     51     static double dis2(const Point &_st,const Point &_se)
     52     {
     53         return (_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y);
     54     }
     55     //两点间距离
     56     static double dis(const Point &_st, const Point &_se)
     57     {
     58         return sqrt((_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y));
     59     }
     60 };
     61 //两点表示的向量
     62 class Line
     63 {
     64 public:
     65     Point s, e;//两点表示,起点[s],终点[e]
     66     double a, b, c;//一般式,ax+by+c=0
     67 
     68     Line(){}
     69     Line(const Point &s, const Point &e):s(s),e(e){}
     70     Line(double _a,double _b,double _c):a(_a),b(_b),c(_c){}
     71 
     72     //向量与点的叉乘,参数:点[_Off]
     73     //[点相对向量位置判断]
     74     double operator /(const Point &_Off) const
     75     {
     76         return (_Off.y - s.y) * (e.x - s.x) - (_Off.x - s.x) * (e.y - s.y);
     77     }
     78     //向量与向量的叉乘,参数:向量[_Off]
     79     friend double operator /(const Line &_st,const Line &_se)
     80     {
     81         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);
     82     }
     83     friend double operator *(const Line &_st,const Line &_se)
     84     {
     85         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);
     86     }
     87     //从两点表示转换为一般表示
     88     //a=y2-y1,b=x1-x2,c=x2*y1-x1*y2
     89     bool pton()
     90     {
     91         a = e.y - s.y;
     92         b = s.x - e.x;
     93         c = e.x * s.y - e.y * s.x;
     94         return true;
     95     }
     96 
     97     //-----------点和直线(向量)-----------
     98     //点在向量左边(右边的小于号改成大于号即可,在对应直线上则加上=号)
     99     //参数:点[_Off],向量[_Ori]
    100     friend bool operator<(const Point &_Off, const Line &_Ori)
    101     {
    102         return (_Ori.e.y - _Ori.s.y) * (_Off.x - _Ori.s.x)
    103             < (_Off.y - _Ori.s.y) * (_Ori.e.x - _Ori.s.x);
    104     }
    105 
    106     //点在直线上,参数:点[_Off]
    107     bool lhas(const Point &_Off) const
    108     {
    109         return Point::sgn((*this) / _Off, 0) == 0;
    110     }
    111     //点在线段上,参数:点[_Off]
    112     bool shas(const Point &_Off) const
    113     {
    114         return lhas(_Off)
    115             && Point::sgn(_Off.x - min(s.x, e.x), 0) > 0 && Point::sgn(_Off.x - max(s.x, e.x), 0) < 0
    116             && Point::sgn(_Off.y - min(s.y, e.y), 0) > 0 && Point::sgn(_Off.y - max(s.y, e.y), 0) < 0;
    117     }
    118 
    119     //点到直线/线段的距离
    120     //参数: 点[_Off], 是否是线段[isSegment](默认为直线)
    121     double dis(const Point &_Off, bool isSegment = false)
    122     {
    123         ///化为一般式
    124         pton();
    125 
    126         //到直线垂足的距离
    127         double td = (a * _Off.x + b * _Off.y + c) / sqrt(a * a + b * b);
    128 
    129         //如果是线段判断垂足
    130         if(isSegment)
    131         {
    132             double xp = (b * b * _Off.x - a * b * _Off.y - a * c) / ( a * a + b * b);
    133             double yp = (-a * b * _Off.x + a * a * _Off.y - b * c) / (a * a + b * b);
    134             double xb = max(s.x, e.x);
    135             double yb = max(s.y, e.y);
    136             double xs = s.x + e.x - xb;
    137             double ys = s.y + e.y - yb;
    138             if(xp > xb + eps || xp < xs - eps || yp > yb + eps || yp < ys - eps)
    139                 td = min(Point::dis(_Off,s), Point::dis(_Off,e));
    140         }
    141 
    142         return fabs(td);
    143     }
    144 
    145     //关于直线对称的点
    146     Point mirror(const Point &_Off) const
    147     {
    148         ///注意先转为一般式
    149         Point ret;
    150         double d = a * a + b * b;
    151         ret.x = (b * b * _Off.x - a * a * _Off.x - 2 * a * b * _Off.y - 2 * a * c) / d;
    152         ret.y = (a * a * _Off.y - b * b * _Off.y - 2 * a * b * _Off.x - 2 * b * c) / d;
    153         return ret;
    154     }
    155     //计算两点的中垂线
    156     static Line ppline(const Point &_a, const Point &_b)
    157     {
    158         Line ret;
    159         ret.s.x = (_a.x + _b.x) / 2;
    160         ret.s.y = (_a.y + _b.y) / 2;
    161         //一般式
    162         ret.a = _b.x - _a.x;
    163         ret.b = _b.y - _a.y;
    164         ret.c = (_a.y - _b.y) * ret.s.y + (_a.x - _b.x) * ret.s.x;
    165         //两点式
    166         if(fabs(ret.a) > eps)
    167         {
    168             ret.e.y = 0.0;
    169             ret.e.x = - ret.c / ret.a;
    170             if(ret.e == ret. s)
    171             {
    172                 ret.e.y = 1e10;
    173                 ret.e.x = - (ret.c - ret.b * ret.e.y) / ret.a;
    174             }
    175         }
    176         else
    177         {
    178             ret.e.x = 0.0;
    179             ret.e.y = - ret.c / ret.b;
    180             if(ret.e == ret. s)
    181             {
    182                 ret.e.x = 1e10;
    183                 ret.e.y = - (ret.c - ret.a * ret.e.x) / ret.b;
    184             }
    185         }
    186         return ret;
    187     }
    188 
    189     //------------直线和直线(向量)-------------
    190     //直线重合,参数:直线向量[_st],[_se]
    191     static bool equal(const Line &_st, const Line &_se)
    192     {
    193         return _st.lhas(_se.e) && _se.lhas(_se.s);
    194     }
    195     //直线平行,参数:直线向量[_st],[_se]
    196     static bool parallel(const Line &_st,const Line &_se)
    197     {
    198         return Point::sgn(_st / _se, 0) == 0;
    199     }
    200     //两直线(线段)交点,参数:直线向量[_st],[_se],交点
    201     //返回-1代表平行,0代表重合,1代表相交
    202     static bool crossLPt(const Line &_st,const Line &_se,Point &ret)
    203     {
    204         if(Line::parallel(_st,_se))
    205         {
    206             if(Line::equal(_st,_se)) return 0;
    207             return -1;
    208         }
    209         ret = _st.s;
    210         double t = (Line(_st.s,_se.s)/_se)/(_st/_se);
    211         ret.x += (_st.e.x - _st.s.x) * t;
    212         ret.y += (_st.e.y - _st.s.y) * t;
    213         return 1;
    214     }
    215     //------------线段和直线(向量)----------
    216     //线段和直线交
    217     //参数:直线[_st],线段[_se]
    218     friend bool crossSL(const Line &_st,const Line &_se)
    219     {
    220         return Point::sgn((_st / _se.s) * (_st / _se.e) ,0) <= 0;
    221     }
    222 
    223     //------------线段和线段(向量)----------
    224     //判断线段是否相交(注意添加eps),参数:线段[_st],线段[_se]
    225     static bool isCrossSS(const Line &_st,const Line &_se)
    226     {
    227         //1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交
    228         //2.跨立试验(等于0时端点重合)
    229         return
    230             max(_st.s.x, _st.e.x) >= min(_se.s.x, _se.e.x) &&
    231             max(_se.s.x, _se.e.x) >= min(_st.s.x, _st.e.x) &&
    232             max(_st.s.y, _st.e.y) >= min(_se.s.y, _se.e.y) &&
    233             max(_se.s.y, _se.e.y) >= min(_st.s.y, _st.e.y) &&
    234             Point::sgn((_st / Line(_st.s, _se.s)) * (_st / Line(_st.s, _se.e)), 0) <= 0 &&
    235             Point::sgn((_se / Line(_se.s, _st.s)) * (_se / Line(_se.s, _st.e)), 0) <= 0;
    236     }
    237 };
    238 class Polygon
    239 {
    240 public:
    241     const static int maxpn = 100;
    242     Point pt[maxpn];//点(顺时针或逆时针)
    243     int n;//点的个数
    244 
    245     Point& operator[](int _p)
    246     {
    247         return pt[_p];
    248     }
    249 
    250     //求多边形面积,多边形内点必须顺时针或逆时针
    251     double area() const
    252     {
    253         double ans = 0.0;
    254         for(int i = 0; i < n; i ++)
    255         {
    256             int nt = (i + 1) % n;
    257             ans += pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
    258         }
    259         return fabs(ans / 2.0);
    260     }
    261     //求多边形重心,多边形内点必须顺时针或逆时针
    262     Point gravity() const
    263     {
    264         Point ans;
    265         ans.x = ans.y = 0.0;
    266         double area = 0.0;
    267         for(int i = 0; i < n; i ++)
    268         {
    269             int nt = (i + 1) % n;
    270             double tp = pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
    271             area += tp;
    272             ans.x += tp * (pt[i].x + pt[nt].x);
    273             ans.y += tp * (pt[i].y + pt[nt].y);
    274         }
    275         ans.x /= 3 * area;
    276         ans.y /= 3 * area;
    277         return ans;
    278     }
    279     //判断点在凸多边形内,参数:点[_Off]
    280     bool chas(const Point &_Off) const
    281     {
    282         double tp = 0, np;
    283         for(int i = 0; i < n; i ++)
    284         {
    285             np = Line(pt[i], pt[(i + 1) % n]) / _Off;
    286             if(tp * np < -eps)
    287                 return false;
    288             tp = (fabs(np) > eps)?np: tp;
    289         }
    290         return true;
    291     }
    292     //判断点是否在任意多边形内[射线法],O(n)
    293     bool ahas(const Point &_Off) const
    294     {
    295         int ret = 0;
    296         double infv = 1e-10;//坐标系最大范围
    297         Line l = Line(_Off, Point( -infv ,_Off.y));
    298         for(int i = 0; i < n; i ++)
    299         {
    300             Line ln = Line(pt[i], pt[(i + 1) % n]);
    301             if(fabs(ln.s.y - ln.e.y) > eps)
    302             {
    303                 Point tp = (ln.s.y > ln.e.y)? ln.s: ln.e;
    304                 if(fabs(tp.y - _Off.y) < eps && tp.x < _Off.x + eps)
    305                     ret ++;
    306             }
    307             else if(Line::isCrossSS(ln,l))
    308                 ret ++;
    309         }
    310         return (ret % 2 == 1);
    311     }
    312     //凸多边形被直线分割,参数:直线[_Off]
    313     Polygon split(Line _Off)
    314     {
    315         //注意确保多边形能被分割
    316         Polygon ret;
    317         Point spt[2];
    318         double tp = 0.0, np;
    319         bool flag = true;
    320         int i, pn = 0, spn = 0;
    321         for(i = 0; i < n; i ++)
    322         {
    323             if(flag)
    324                 pt[pn ++] = pt[i];
    325             else
    326                 ret.pt[ret.n ++] = pt[i];
    327             np = _Off / pt[(i + 1) % n];
    328             if(tp * np < -eps)
    329             {
    330                 flag = !flag;
    331                 Line::crossLPt(_Off,Line(pt[i], pt[(i + 1) % n]),spt[spn++]);
    332             }
    333             tp = (fabs(np) > eps)?np: tp;
    334         }
    335         ret.pt[ret.n ++] = spt[0];
    336         ret.pt[ret.n ++] = spt[1];
    337         n = pn;
    338         return ret;
    339     }
    340 
    341 
    342     /** 卷包裹法求点集凸包,_p为输入点集,_n为点的数量 **/
    343     void ConvexClosure(Point _p[],int _n)
    344     {
    345         sort(_p,_p+_n);
    346         n=0;
    347         for(int i=0;i<_n;i++)
    348         {
    349             while(n>1&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<=0)
    350                 n--;
    351             pt[n++]=_p[i];
    352         }
    353         int _key=n;
    354         for(int i=_n-2;i>=0;i--)
    355         {
    356             while(n>_key&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<=0)
    357                 n--;
    358             pt[n++]=_p[i];
    359         }
    360         if(n>1)   n--;//除去重复的点,该点已是凸包凸包起点
    361     }
    362 //    /****** 寻找凸包的graham 扫描法********************/
    363 //    /****** _p为输入的点集,_n为点的数量****************/
    364 //    /**使用时需把gmp函数放在类外,并且看情况修改pt[0]**/
    365 //    bool gcmp(const Point &ta,const Point &tb)/// 选取与最后一条确定边夹角最小的点,即余弦值最大者
    366 //    {
    367 //        double tmp=Line(pt[0],ta)/Line(pt[0],tb);
    368 //        if(Point::sgn(tmp,0)==0)
    369 //            return Point::dis(pt[0],ta)<Point::dis(pt[0],tb);
    370 //        else if(tmp>0)
    371 //            return 1;
    372 //        return 0;
    373 //    }
    374 //    void graham(Point _p[],int _n)
    375 //    {
    376 //        int cur=0;
    377 //        for(int i=1;i<_n;i++)
    378 //            if(Point::sgn(_p[cur].y,_p[i].y)>0 || (Point::sgn(_p[cur].y,_p[i].y)==0 && Point::sgn(_p[cur].x,_p[i].x)>0))
    379 //                cur=i;
    380 //        swap(_p[cur],_p[0]);
    381 //        n=0,pt[n++]=_p[0];
    382 //        if(_n==1)   return;
    383 //        sort(_p+1,_p+_n,Polygon::gcmp);
    384 //        pt[n++]=_p[1],pt[n++]=_p[2];
    385 //        for(int i=3;i<_n;i++)
    386 //        {
    387 //            while(Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<0)
    388 //                n--;
    389 //            pt[n++]=_p[i];
    390 //        }
    391 //    }
    392     //凸包旋转卡壳(注意点必须顺时针或逆时针排列)
    393     //返回值凸包直径的平方(最远两点距离的平方)
    394     double rotating_calipers()
    395     {
    396         int i = 1;
    397         double ret = 0.0;
    398         pt[n] = pt[0];
    399         for(int j = 0; j < n; j ++)
    400         {
    401             while(fabs(Point::xmult(pt[j], pt[j + 1], pt[i + 1])) > fabs(Point::xmult(pt[j], pt[j + 1], pt[i])) + eps)
    402                 i = (i + 1) % n;
    403             //pt[i]和pt[j],pt[i + 1]和pt[j + 1]可能是对踵点
    404             ret = max(ret, max(Point::dis(pt[i],pt[j]), Point::dis(pt[i + 1],pt[j + 1])));
    405         }
    406         return ret;
    407     }
    408 
    409     //凸包旋转卡壳(注意点必须逆时针排列)
    410     //返回值两凸包的最短距离
    411     double rotating_calipers(Polygon &_Off)
    412     {
    413         int i = 0;
    414         double ret = 1e10;//inf
    415         pt[n] = pt[0];
    416         _Off.pt[_Off.n] = _Off.pt[0];
    417         //注意凸包必须逆时针排列且pt[0]是左下角点的位置
    418         while(_Off.pt[i + 1].y > _Off.pt[i].y)
    419             i = (i + 1) % _Off.n;
    420         for(int j = 0; j < n; j ++)
    421         {
    422             double tp;
    423             //逆时针时为 >,顺时针则相反
    424             while((tp = Point::xmult(pt[j], pt[j + 1], _Off.pt[i + 1]) - Point::xmult( pt[j], pt[j + 1], _Off.pt[i])) > eps)
    425                 i = (i + 1) % _Off.n;
    426             //(pt[i],pt[i+1])和(_Off.pt[j],_Off.pt[j + 1])可能是最近线段
    427             ret = min(ret, Line(pt[j], pt[j + 1]).dis(_Off.pt[i], true));
    428             ret = min(ret, Line(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j + 1], true));
    429             if(tp > -eps)//如果不考虑TLE问题最好不要加这个判断
    430             {
    431                 ret = min(ret, Line(pt[j], pt[j + 1]).dis(_Off.pt[i + 1], true));
    432                 ret = min(ret, Line(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j], true));
    433             }
    434         }
    435         return ret;
    436     }
    437 
    438     //-----------半平面交-------------
    439     //复杂度:O(nlog2(n))
    440     //#include <algorithm>
    441     //半平面计算极角函数[如果考虑效率可以用成员变量记录]
    442     static double hpc_pa(const Line &_Off)
    443     {
    444         return atan2(_Off.e.y - _Off.s.y, _Off.e.x - _Off.s.x);
    445     }
    446     //半平面交排序函数[优先顺序: 1.极角 2.前面的直线在后面的左边]
    447     static bool hpc_cmp(const Line &l, const Line &r)
    448     {
    449         double lp = hpc_pa(l), rp = hpc_pa(r);
    450         if(fabs(lp - rp) > eps)
    451             return lp < rp;
    452         return Point::xmult(l.s, r.e, r.s) < -eps;
    453     }
    454     static int judege(const Line &_lx,const Line &_ly,const Line &_lz)
    455     {
    456         Point tmp;
    457         Line::crossLPt(_lx,_ly,tmp);
    458         return Point::sgn(Point::xmult(tmp,_lz.e,_lz.s),0);
    459     }
    460     //获取半平面交的多边形(多边形的核)
    461     //参数:向量集合[l],向量数量[ln];(半平面方向在向量左边)
    462     //函数运行后如果n[即返回多边形的点数量]为0则不存在半平面交的多边形(不存在区域或区域面积无穷大)
    463     Polygon& halfPanelCross(Line _Off[], int ln)
    464     {
    465         Line dequeue[maxpn];//用于计算的双端队列
    466         int i, tn;
    467         sort(_Off, _Off + ln, hpc_cmp);
    468         //平面在向量左边的筛选
    469         for(i = tn = 1; i < ln; i ++)
    470             if(fabs(hpc_pa(_Off[i]) - hpc_pa(_Off[i - 1])) > eps)
    471                 _Off[tn ++] = _Off[i];
    472         ln = tn,n = 0;
    473         int bot = 0, top = 1;
    474         dequeue[0] = _Off[0];
    475         dequeue[1] = _Off[1];
    476         //for(int i=0;i<ln;i++)
    477         //    printf("%.2f %.2f %.2f %.2f
    ",_Off[i].s.x,_Off[i].s.y,_Off[i].e.x,_Off[i].e.y);
    478         for(i = 2; i < ln; i ++)
    479         {
    480             //printf("%.2f
    ",Polygon::judege(dequeue[top],dequeue[top-1],_Off[i]));
    481             while(bot < top &&  Polygon::judege(dequeue[top],dequeue[top-1],_Off[i]) > 0)
    482                 top --;
    483             while(bot < top &&  Polygon::judege(dequeue[bot],dequeue[bot+1],_Off[i]) > 0)
    484                 bot ++;
    485             dequeue[++ top] = _Off[i];
    486         }
    487 
    488         while(bot < top && Polygon::judege(dequeue[top],dequeue[top-1],dequeue[bot]) > 0)
    489             top --;
    490         while(bot < top && Polygon::judege(dequeue[bot],dequeue[bot+1],dequeue[top]) > 0)
    491             bot ++;
    492         //计算交点(注意不同直线形成的交点可能重合)
    493         if(top <= bot + 1)
    494             return (*this);
    495         for(i = bot; i < top; i ++)
    496             Line::crossLPt(dequeue[i],dequeue[i + 1],pt[n++]);
    497         if(bot < top + 1)
    498             Line::crossLPt(dequeue[bot],dequeue[top],pt[n++]);
    499         return (*this);
    500     }
    501 };
    502 
    503 int n;
    504 Point pt[200];
    505 Line ln[200];
    506 Polygon ans;
    507 int main(void)
    508 {
    509     int t;cin>>t;
    510     while(t--)
    511     {
    512         cin>>n;
    513         for(int i=0;i<n;i++)
    514             scanf("%lf%lf",&pt[i].x,&pt[i].y);
    515         pt[n++]=pt[0];
    516         for(int i=1;i<n;i++)
    517             ln[i-1]=Line(pt[i],pt[i-1]);
    518         ans.halfPanelCross(ln,n-1);
    519         if(ans.n)
    520             printf("YES
    ");
    521         else
    522             printf("NO
    ");
    523     }
    524     return 0;
    525 }
    View Code
      1 ///Poj 3130
      2 #include <iostream>
      3 #include <cstdio>
      4 #include <cmath>
      5 #include <algorithm>
      6 
      7 
      8 using namespace std;
      9 const double eps = 1e-10;
     10 //
     11 class Point
     12 {
     13 public:
     14     double x, y;
     15 
     16     Point(){}
     17     Point(double x, double y):x(x),y(y){}
     18 
     19     bool operator < (const Point &_se) const
     20     {
     21         return x<_se.x || (x==_se.x && y<_se.y);
     22     }
     23     /*******判断ta与tb的大小关系*******/
     24     static int sgn(double ta,double tb)
     25     {
     26         if(fabs(ta-tb)<eps)return 0;
     27         if(ta<tb)   return -1;
     28         return 1;
     29     }
     30     static double xmult(const Point &po, const Point &ps, const Point &pe)
     31     {
     32         return (ps.x - po.x) * (pe.y - po.y) - (pe.x - po.x) * (ps.y - po.y);
     33     }
     34     friend Point operator + (const Point &_st,const Point &_se)
     35     {
     36         return Point(_st.x + _se.x, _st.y + _se.y);
     37     }
     38     friend Point operator - (const Point &_st,const Point &_se)
     39     {
     40         return Point(_st.x - _se.x, _st.y - _se.y);
     41     }
     42     //点位置相同(double类型)
     43     bool operator == (const Point &_off) const
     44     {
     45         return  Point::sgn(x, _off.x) == 0 && Point::sgn(y, _off.y) == 0;
     46     }
     47     //点位置不同(double类型)
     48     bool operator != (const Point &_Off) const
     49     {
     50         return ((*this) == _Off) == false;
     51     }
     52     //两点间距离的平方
     53     static double dis2(const Point &_st,const Point &_se)
     54     {
     55         return (_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y);
     56     }
     57     //两点间距离
     58     static double dis(const Point &_st, const Point &_se)
     59     {
     60         return sqrt((_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y));
     61     }
     62 };
     63 //两点表示的向量
     64 class Line
     65 {
     66 public:
     67     Point s, e;//两点表示,起点[s],终点[e]
     68     double a, b, c;//一般式,ax+by+c=0
     69 
     70     Line(){}
     71     Line(const Point &s, const Point &e):s(s),e(e){}
     72     Line(double _a,double _b,double _c):a(_a),b(_b),c(_c){}
     73 
     74     //向量与点的叉乘,参数:点[_Off]
     75     //[点相对向量位置判断]
     76     double operator /(const Point &_Off) const
     77     {
     78         return (_Off.y - s.y) * (e.x - s.x) - (_Off.x - s.x) * (e.y - s.y);
     79     }
     80     //向量与向量的叉乘,参数:向量[_Off]
     81     friend double operator /(const Line &_st,const Line &_se)
     82     {
     83         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);
     84     }
     85     friend double operator *(const Line &_st,const Line &_se)
     86     {
     87         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);
     88     }
     89     //从两点表示转换为一般表示
     90     //a=y2-y1,b=x1-x2,c=x2*y1-x1*y2
     91     bool pton()
     92     {
     93         a = e.y - s.y;
     94         b = s.x - e.x;
     95         c = e.x * s.y - e.y * s.x;
     96         return true;
     97     }
     98 
     99     //-----------点和直线(向量)-----------
    100     //点在向量左边(右边的小于号改成大于号即可,在对应直线上则加上=号)
    101     //参数:点[_Off],向量[_Ori]
    102     friend bool operator<(const Point &_Off, const Line &_Ori)
    103     {
    104         return (_Ori.e.y - _Ori.s.y) * (_Off.x - _Ori.s.x)
    105             < (_Off.y - _Ori.s.y) * (_Ori.e.x - _Ori.s.x);
    106     }
    107 
    108     //点在直线上,参数:点[_Off]
    109     bool lhas(const Point &_Off) const
    110     {
    111         return Point::sgn((*this) / _Off, 0) == 0;
    112     }
    113     //点在线段上,参数:点[_Off]
    114     bool shas(const Point &_Off) const
    115     {
    116         return lhas(_Off)
    117             && Point::sgn(_Off.x - min(s.x, e.x), 0) > 0 && Point::sgn(_Off.x - max(s.x, e.x), 0) < 0
    118             && Point::sgn(_Off.y - min(s.y, e.y), 0) > 0 && Point::sgn(_Off.y - max(s.y, e.y), 0) < 0;
    119     }
    120 
    121     //点到直线/线段的距离
    122     //参数: 点[_Off], 是否是线段[isSegment](默认为直线)
    123     double dis(const Point &_Off, bool isSegment = false)
    124     {
    125         ///化为一般式
    126         pton();
    127 
    128         //到直线垂足的距离
    129         double td = (a * _Off.x + b * _Off.y + c) / sqrt(a * a + b * b);
    130 
    131         //如果是线段判断垂足
    132         if(isSegment)
    133         {
    134             double xp = (b * b * _Off.x - a * b * _Off.y - a * c) / ( a * a + b * b);
    135             double yp = (-a * b * _Off.x + a * a * _Off.y - b * c) / (a * a + b * b);
    136             double xb = max(s.x, e.x);
    137             double yb = max(s.y, e.y);
    138             double xs = s.x + e.x - xb;
    139             double ys = s.y + e.y - yb;
    140             if(xp > xb + eps || xp < xs - eps || yp > yb + eps || yp < ys - eps)
    141                 td = min(Point::dis(_Off,s), Point::dis(_Off,e));
    142         }
    143 
    144         return fabs(td);
    145     }
    146 
    147     //关于直线对称的点
    148     Point mirror(const Point &_Off) const
    149     {
    150         ///注意先转为一般式
    151         Point ret;
    152         double d = a * a + b * b;
    153         ret.x = (b * b * _Off.x - a * a * _Off.x - 2 * a * b * _Off.y - 2 * a * c) / d;
    154         ret.y = (a * a * _Off.y - b * b * _Off.y - 2 * a * b * _Off.x - 2 * b * c) / d;
    155         return ret;
    156     }
    157     //计算两点的中垂线
    158     static Line ppline(const Point &_a, const Point &_b)
    159     {
    160         Line ret;
    161         ret.s.x = (_a.x + _b.x) / 2;
    162         ret.s.y = (_a.y + _b.y) / 2;
    163         //一般式
    164         ret.a = _b.x - _a.x;
    165         ret.b = _b.y - _a.y;
    166         ret.c = (_a.y - _b.y) * ret.s.y + (_a.x - _b.x) * ret.s.x;
    167         //两点式
    168         if(std::fabs(ret.a) > eps)
    169         {
    170             ret.e.y = 0.0;
    171             ret.e.x = - ret.c / ret.a;
    172             if(ret.e == ret. s)
    173             {
    174                 ret.e.y = 1e10;
    175                 ret.e.x = - (ret.c - ret.b * ret.e.y) / ret.a;
    176             }
    177         }
    178         else
    179         {
    180             ret.e.x = 0.0;
    181             ret.e.y = - ret.c / ret.b;
    182             if(ret.e == ret. s)
    183             {
    184                 ret.e.x = 1e10;
    185                 ret.e.y = - (ret.c - ret.a * ret.e.x) / ret.b;
    186             }
    187         }
    188         return ret;
    189     }
    190 
    191     //------------直线和直线(向量)-------------
    192     //直线重合,参数:直线向量[_st],[_se]
    193     static bool equal(const Line &_st, const Line &_se)
    194     {
    195         return _st.lhas(_se.e) && _se.lhas(_se.s);
    196     }
    197     //直线平行,参数:直线向量[_st],[_se]
    198     static bool parallel(const Line &_st,const Line &_se)
    199     {
    200         return Point::sgn(_st / _se, 0) == 0;
    201     }
    202     //两直线(线段)交点,参数:直线向量[_st],[_se],交点
    203     //返回-1代表平行,0代表重合,1代表相交
    204     static bool crossLPt(const Line &_st,const Line &_se,Point &ret)
    205     {
    206         if(Line::parallel(_st,_se))
    207         {
    208             if(Line::equal(_st,_se)) return 0;
    209             return -1;
    210         }
    211         ret = _st.s;
    212         double t = (Line(_st.s,_se.s)/_se)/(_st/_se);
    213         ret.x += (_st.e.x - _st.s.x) * t;
    214         ret.y += (_st.e.y - _st.s.y) * t;
    215         return 1;
    216     }
    217     //------------线段和直线(向量)----------
    218     //线段和直线交
    219     //参数:直线[_st],线段[_se]
    220     friend bool crossSL(const Line &_st,const Line &_se)
    221     {
    222         return Point::sgn((_st / _se.s) * (_st / _se.e) ,0) <= 0;
    223     }
    224 
    225     //------------线段和线段(向量)----------
    226     //判断线段是否相交(注意添加eps),参数:线段[_st],线段[_se]
    227     static bool isCrossSS(const Line &_st,const Line &_se)
    228     {
    229         //1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交
    230         //2.跨立试验(等于0时端点重合)
    231         return
    232             max(_st.s.x, _st.e.x) >= min(_se.s.x, _se.e.x) &&
    233             max(_se.s.x, _se.e.x) >= min(_st.s.x, _st.e.x) &&
    234             max(_st.s.y, _st.e.y) >= min(_se.s.y, _se.e.y) &&
    235             max(_se.s.y, _se.e.y) >= min(_st.s.y, _st.e.y) &&
    236             Point::sgn((_st / Line(_st.s, _se.s)) * (_st / Line(_st.s, _se.e)), 0) <= 0 &&
    237             Point::sgn((_se / Line(_se.s, _st.s)) * (_se / Line(_se.s, _st.e)), 0) <= 0;
    238     }
    239 };
    240 class Polygon
    241 {
    242 public:
    243     const static int maxpn = 100;
    244     Point pt[maxpn];//点(顺时针或逆时针)
    245     int n;//点的个数
    246 
    247     Point& operator[](int _p)
    248     {
    249         return pt[_p];
    250     }
    251 
    252     //求多边形面积,多边形内点必须顺时针或逆时针
    253     double area() const
    254     {
    255         double ans = 0.0;
    256         for(int i = 0; i < n; i ++)
    257         {
    258             int nt = (i + 1) % n;
    259             ans += pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
    260         }
    261         return fabs(ans / 2.0);
    262     }
    263     //求多边形重心,多边形内点必须顺时针或逆时针
    264     Point gravity() const
    265     {
    266         Point ans;
    267         ans.x = ans.y = 0.0;
    268         double area = 0.0;
    269         for(int i = 0; i < n; i ++)
    270         {
    271             int nt = (i + 1) % n;
    272             double tp = pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
    273             area += tp;
    274             ans.x += tp * (pt[i].x + pt[nt].x);
    275             ans.y += tp * (pt[i].y + pt[nt].y);
    276         }
    277         ans.x /= 3 * area;
    278         ans.y /= 3 * area;
    279         return ans;
    280     }
    281     //判断点在凸多边形内,参数:点[_Off]
    282     bool chas(const Point &_Off) const
    283     {
    284         double tp = 0, np;
    285         for(int i = 0; i < n; i ++)
    286         {
    287             np = Line(pt[i], pt[(i + 1) % n]) / _Off;
    288             if(tp * np < -eps)
    289                 return false;
    290             tp = (fabs(np) > eps)?np: tp;
    291         }
    292         return true;
    293     }
    294     //判断点是否在任意多边形内[射线法],O(n)
    295     bool ahas(const Point &_Off) const
    296     {
    297         int ret = 0;
    298         double infv = 1e-10;//坐标系最大范围
    299         Line l = Line(_Off, Point( -infv ,_Off.y));
    300         for(int i = 0; i < n; i ++)
    301         {
    302             Line ln = Line(pt[i], pt[(i + 1) % n]);
    303             if(fabs(ln.s.y - ln.e.y) > eps)
    304             {
    305                 Point tp = (ln.s.y > ln.e.y)? ln.s: ln.e;
    306                 if(fabs(tp.y - _Off.y) < eps && tp.x < _Off.x + eps)
    307                     ret ++;
    308             }
    309             else if(Line::isCrossSS(ln,l))
    310                 ret ++;
    311         }
    312         return (ret % 2 == 1);
    313     }
    314     //凸多边形被直线分割,参数:直线[_Off]
    315     Polygon split(Line _Off)
    316     {
    317         //注意确保多边形能被分割
    318         Polygon ret;
    319         Point spt[2];
    320         double tp = 0.0, np;
    321         bool flag = true;
    322         int i, pn = 0, spn = 0;
    323         for(i = 0; i < n; i ++)
    324         {
    325             if(flag)
    326                 pt[pn ++] = pt[i];
    327             else
    328                 ret.pt[ret.n ++] = pt[i];
    329             np = _Off / pt[(i + 1) % n];
    330             if(tp * np < -eps)
    331             {
    332                 flag = !flag;
    333                 Line::crossLPt(_Off,Line(pt[i], pt[(i + 1) % n]),spt[spn++]);
    334             }
    335             tp = (fabs(np) > eps)?np: tp;
    336         }
    337         ret.pt[ret.n ++] = spt[0];
    338         ret.pt[ret.n ++] = spt[1];
    339         n = pn;
    340         return ret;
    341     }
    342 
    343 
    344     /** 卷包裹法求点集凸包,_p为输入点集,_n为点的数量 **/
    345     void ConvexClosure(Point _p[],int _n)
    346     {
    347         sort(_p,_p+_n);
    348         n=0;
    349         for(int i=0;i<_n;i++)
    350         {
    351             while(n>1&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<=0)
    352                 n--;
    353             pt[n++]=_p[i];
    354         }
    355         int _key=n;
    356         for(int i=_n-2;i>=0;i--)
    357         {
    358             while(n>_key&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<=0)
    359                 n--;
    360             pt[n++]=_p[i];
    361         }
    362         if(n>1)   n--;//除去重复的点,该点已是凸包凸包起点
    363     }
    364 //    /****** 寻找凸包的graham 扫描法********************/
    365 //    /****** _p为输入的点集,_n为点的数量****************/
    366 //    /**使用时需把gmp函数放在类外,并且看情况修改pt[0]**/
    367 //    bool gcmp(const Point &ta,const Point &tb)/// 选取与最后一条确定边夹角最小的点,即余弦值最大者
    368 //    {
    369 //        double tmp=Line(pt[0],ta)/Line(pt[0],tb);
    370 //        if(Point::sgn(tmp,0)==0)
    371 //            return Point::dis(pt[0],ta)<Point::dis(pt[0],tb);
    372 //        else if(tmp>0)
    373 //            return 1;
    374 //        return 0;
    375 //    }
    376 //    void graham(Point _p[],int _n)
    377 //    {
    378 //        int cur=0;
    379 //        for(int i=1;i<_n;i++)
    380 //            if(Point::sgn(_p[cur].y,_p[i].y)>0 || (Point::sgn(_p[cur].y,_p[i].y)==0 && Point::sgn(_p[cur].x,_p[i].x)>0))
    381 //                cur=i;
    382 //        swap(_p[cur],_p[0]);
    383 //        n=0,pt[n++]=_p[0];
    384 //        if(_n==1)   return;
    385 //        sort(_p+1,_p+_n,Polygon::gcmp);
    386 //        pt[n++]=_p[1],pt[n++]=_p[2];
    387 //        for(int i=3;i<_n;i++)
    388 //        {
    389 //            while(Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<0)
    390 //                n--;
    391 //            pt[n++]=_p[i];
    392 //        }
    393 //    }
    394     //凸包旋转卡壳(注意点必须顺时针或逆时针排列)
    395     //返回值凸包直径的平方(最远两点距离的平方)
    396     double rotating_calipers()
    397     {
    398         int i = 1;
    399         double ret = 0.0;
    400         pt[n] = pt[0];
    401         for(int j = 0; j < n; j ++)
    402         {
    403             while(fabs(Point::xmult(pt[i+1],pt[j], pt[j + 1])) > fabs(Point::xmult(pt[i],pt[j], pt[j + 1])) + eps)
    404                 i = (i + 1) % n;
    405             //pt[i]和pt[j],pt[i + 1]和pt[j + 1]可能是对踵点
    406             ret = (ret, max(Point::dis(pt[i],pt[j]), Point::dis(pt[i + 1],pt[j + 1])));
    407         }
    408         return ret;
    409     }
    410 
    411     //凸包旋转卡壳(注意点必须逆时针排列)
    412     //返回值两凸包的最短距离
    413     double rotating_calipers(Polygon &_Off)
    414     {
    415         int i = 0;
    416         double ret = 1e10;//inf
    417         pt[n] = pt[0];
    418         _Off.pt[_Off.n] = _Off.pt[0];
    419         //注意凸包必须逆时针排列且pt[0]是左下角点的位置
    420         while(_Off.pt[i + 1].y > _Off.pt[i].y)
    421             i = (i + 1) % _Off.n;
    422         for(int j = 0; j < n; j ++)
    423         {
    424             double tp;
    425             //逆时针时为 >,顺时针则相反
    426             while((tp = Point::xmult(_Off.pt[i + 1],pt[j], pt[j + 1]) - Point::xmult(_Off.pt[i], pt[j], pt[j + 1])) > eps)
    427                 i = (i + 1) % _Off.n;
    428             //(pt[i],pt[i+1])和(_Off.pt[j],_Off.pt[j + 1])可能是最近线段
    429             ret = min(ret, Line(pt[j], pt[j + 1]).dis(_Off.pt[i], true));
    430             ret = min(ret, Line(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j + 1], true));
    431             if(tp > -eps)//如果不考虑TLE问题最好不要加这个判断
    432             {
    433                 ret = min(ret, Line(pt[j], pt[j + 1]).dis(_Off.pt[i + 1], true));
    434                 ret = min(ret, Line(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j], true));
    435             }
    436         }
    437         return ret;
    438     }
    439 
    440     //-----------半平面交-------------
    441     //复杂度:O(nlog2(n))
    442     //#include <algorithm>
    443     //半平面计算极角函数[如果考虑效率可以用成员变量记录]
    444     static double hpc_pa(const Line &_Off)
    445     {
    446         return atan2(_Off.e.y - _Off.s.y, _Off.e.x - _Off.s.x);
    447     }
    448     //半平面交排序函数[优先顺序: 1.极角 2.前面的直线在后面的左边]
    449     static bool hpc_cmp(const Line &l, const Line &r)
    450     {
    451         double lp = hpc_pa(l), rp = hpc_pa(r);
    452         if(fabs(lp - rp) > eps)
    453             return lp < rp;
    454         return Point::xmult(r.s,l.s, r.e) < -eps;
    455     }
    456     static int judege(const Line &_lx,const Line &_ly,const Line &_lz)
    457     {
    458         Point tmp;
    459         Line::crossLPt(_lx,_ly,tmp);
    460         return Point::sgn(Point::xmult(_lz.s,tmp,_lz.e),0);
    461     }
    462     //获取半平面交的多边形(多边形的核)
    463     //参数:向量集合[l],向量数量[ln];(半平面方向在向量左边)
    464     //函数运行后如果n[即返回多边形的点数量]为0则不存在半平面交的多边形(不存在区域或区域面积无穷大)
    465     Polygon& halfPanelCross(Line _Off[], int ln)
    466     {
    467         Line dequeue[maxpn];//用于计算的双端队列
    468         int i, tn;
    469         sort(_Off, _Off + ln, hpc_cmp);
    470         //平面在向量左边的筛选
    471         for(i = tn = 1; i < ln; i ++)
    472             if(fabs(hpc_pa(_Off[i]) - hpc_pa(_Off[i - 1])) > eps)
    473                 _Off[tn ++] = _Off[i];
    474         ln = tn,n = 0;
    475         int bot = 0, top = 1;
    476         dequeue[0] = _Off[0];
    477         dequeue[1] = _Off[1];
    478         for(i = 2; i < ln; i ++)
    479         {
    480             while(bot < top &&  Polygon::judege(dequeue[top],dequeue[top-1],_Off[i]) > 0)
    481                 top --;
    482             while(bot < top &&  Polygon::judege(dequeue[bot],dequeue[bot+1],_Off[i]) > 0)
    483                 bot ++;
    484             dequeue[++ top] = _Off[i];
    485         }
    486 
    487         while(bot < top && Polygon::judege(dequeue[top],dequeue[top-1],dequeue[bot]) > 0)
    488             top --;
    489         while(bot < top && Polygon::judege(dequeue[bot],dequeue[bot+1],dequeue[top]) > 0)
    490             bot ++;
    491         //计算交点(注意不同直线形成的交点可能重合)
    492         if(top <= bot + 1)
    493             return (*this);
    494         for(i = bot; i < top; i ++)
    495             Line::crossLPt(dequeue[i],dequeue[i + 1],pt[n++]);
    496         if(bot < top + 1)
    497             Line::crossLPt(dequeue[bot],dequeue[top],pt[n++]);
    498         return (*this);
    499     }
    500 };
    501 
    502 int n;
    503 Point pt[100];
    504 Line ln[100];
    505 Polygon ans;
    506 int main(void)
    507 {
    508     while(scanf("%d",&n)&&n)
    509     {
    510         for(int i=0;i<n;i++)
    511             scanf("%lf%lf",&pt[i].x,&pt[i].y);
    512         pt[n++]=pt[0];
    513         for(int i=0;i<n-1;i++)
    514             ln[i]=Line(pt[i],pt[i+1]);
    515         //for(int i=0;i<n-1;i++)
    516         //    printf("%.2f %.2f %.2f %.2f
    ",ln[i].s.x,ln[i].s.y,ln[i].e.x,ln[i].e.y);
    517         ans.halfPanelCross(ln,n-1);
    518         int ff=1;
    519         if(ans.n==0)
    520             ff=0;
    521         else
    522         {
    523             //for(int i=1;i<ans.n;i++)
    524             //if(ans.pt[0]!=ans.pt[i])
    525             //{
    526             //    ff=0;break;
    527             //}
    528         }
    529         printf("%d
    ",ff);
    530     }
    531     return 0;
    532 }
    View Code
  • 相关阅读:
    【转】SVN与Git比较
    我遇到了Hibernate异常
    使用 Eclipse 远程调试 Java 应用程序
    linux显示桌面快捷键设置
    Ubuntu共享WiFi(AP)给Android方法
    用zd1211+Ubuntu 10.04实现的AP
    Ubuntu开机自动禁用无线网络
    戴尔大力宣传Ubuntu 对比与Windows的差异
    【SSH进阶之路】Spring的AOP逐层深入——采用注解完成AOP(七)
    【SSH进阶之路】Spring的AOP逐层深入——AOP的基本原理(六)
  • 原文地址:https://www.cnblogs.com/weeping/p/6372134.html
Copyright © 2020-2023  润新知