三道半平面交测模板题 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\n",_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\n",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\n",cse++);
525         //printf("====%d\n",ans.n);
526         //for(int i=0;i<ans.n;i++)
527         //printf("%.2f %.2f\n",ans[i].x,ans[i].y);
528         if(ans.n)
529             printf("Surveillance is possible.\n\n");
530         else
531             printf("Surveillance is impossible.\n\n");
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\n",_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\n",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\n");
521         else
522             printf("NO\n");
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\n",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\n",ff);
530     }
531     return 0;
532 }
View Code

 

posted @ 2017-02-06 23:13  weeping  阅读(385)  评论(0编辑  收藏  举报