三道半平面交测模板题 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 }
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 }
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 }
作者:weeping
出处:www.cnblogs.com/weeping/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。