1. 程式人生 > >poj1228 Grandpa's Estate

poj1228 Grandpa's Estate

using rom sce for each nal 感覺 problem mis 直線

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

題目:

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

Description

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

Input

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

Output

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

Sample Input

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

Sample Output

NO

Source

Tehran 2002 Preliminary 思路:   題意好迷,讓你判斷凸包是否是穩定凸包。   穩定凸包:任意一條邊上至少有3個點。      我感覺的板子好啰嗦,我要炸了,我要重寫板子!!!
#include <iostream>
#include 
<cstdio> #include <cmath> #include <algorithm> using namespace std; const double PI = acos(-1.0); const double eps = 1e-10; // class Point { public: double x, y; Point(){} Point(double x, double y):x(x),y(y){} bool operator < (const Point &_se) const { return x<_se.x || (x==_se.x && y<_se.y); } /*******判斷ta與tb的大小關系*******/ static int sgn(double ta,double tb) { if(fabs(ta-tb)<eps)return 0; if(ta<tb) return -1; return 1; } static double xmult(const Point &po, const Point &ps, const Point &pe) { return (ps.x - po.x) * (pe.y - po.y) - (pe.x - po.x) * (ps.y - po.y); } friend Point operator + (const Point &_st,const Point &_se) { return Point(_st.x + _se.x, _st.y + _se.y); } friend Point operator - (const Point &_st,const Point &_se) { return Point(_st.x - _se.x, _st.y - _se.y); } //點位置相同(double類型) bool operator == (const Point &_off) const { return Point::sgn(x, _off.x) == 0 && Point::sgn(y, _off.y) == 0; } //點位置不同(double類型) bool operator != (const Point &_Off) const { return ((*this) == _Off) == false; } //兩點間距離的平方 static double dis2(const Point &_st,const Point &_se) { return (_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y); } //兩點間距離 static double dis(const Point &_st, const Point &_se) { return sqrt((_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y)); } }; //兩點表示的向量 class Line { public: Point s, e;//兩點表示,起點[s],終點[e] double a, b, c;//一般式,ax+by+c=0 double angle;//向量的角度,[-pi,pi] Line(){} Line(const Point &s, const Point &e):s(s),e(e){get_angle(1);} Line(double _a,double _b,double _c):a(_a),b(_b),c(_c){} //向量與點的叉乘,參數:點[_Off] //[點相對向量位置判斷] double operator /(const Point &_Off) const { return (_Off.y - s.y) * (e.x - s.x) - (_Off.x - s.x) * (e.y - s.y); } //向量與向量的叉乘,參數:向量[_Off] friend double operator /(const Line &_st,const Line &_se) { return (_st.e.x - _st.s.x) * (_se.e.y - _se.s.y) - (_st.e.y - _st.s.y) * (_se.e.x - _se.s.x); } friend double operator *(const Line &_st,const Line &_se) { return (_st.e.x - _st.s.x) * (_se.e.x - _se.s.x) - (_st.e.y - _st.s.y) * (_se.e.y - _se.s.y); } //從兩點表示轉換為一般表示 //a=y2-y1,b=x1-x2,c=x2*y1-x1*y2 bool pton() { a = e.y - s.y; b = s.x - e.x; c = e.x * s.y - e.y * s.x; return true; } //求直線或向量的角度 double get_angle(bool isVector) { angle=atan2(e.y-s.y,e.x-s.x); if(!isVector && angle<0) angle+=PI; return angle; } // bool operator < (const Line &ta)const { return angle<ta.angle; } //-----------點和直線(向量)----------- //點在向量左邊(右邊的小於號改成大於號即可,在對應直線上則加上=號) //參數:點[_Off],向量[_Ori] friend bool operator<(const Point &_Off, const Line &_Ori) { return (_Ori.e.y - _Ori.s.y) * (_Off.x - _Ori.s.x) < (_Off.y - _Ori.s.y) * (_Ori.e.x - _Ori.s.x); } //點在直線上,參數:點[_Off] bool lhas(const Point &_Off) const { return Point::sgn((*this) / _Off, 0) == 0; } //點在線段上,參數:點[_Off] bool shas(const Point &_Off) const { return lhas(_Off) && Point::sgn(_Off.x - min(s.x, e.x), 0) > 0 && Point::sgn(_Off.x - max(s.x, e.x), 0) < 0 && Point::sgn(_Off.y - min(s.y, e.y), 0) > 0 && Point::sgn(_Off.y - max(s.y, e.y), 0) < 0; } //點到直線/線段的距離 //參數: 點[_Off], 是否是線段[isSegment](默認為直線) double dis(const Point &_Off, bool isSegment = false) { ///化為一般式 pton(); //到直線垂足的距離 double td = (a * _Off.x + b * _Off.y + c) / sqrt(a * a + b * b); //如果是線段判斷垂足 if(isSegment) { double xp = (b * b * _Off.x - a * b * _Off.y - a * c) / ( a * a + b * b); double yp = (-a * b * _Off.x + a * a * _Off.y - b * c) / (a * a + b * b); double xb = max(s.x, e.x); double yb = max(s.y, e.y); double xs = s.x + e.x - xb; double ys = s.y + e.y - yb; if(xp > xb + eps || xp < xs - eps || yp > yb + eps || yp < ys - eps) td = min(Point::dis(_Off,s), Point::dis(_Off,e)); } return fabs(td); } //關於直線對稱的點 Point mirror(const Point &_Off) const { ///註意先轉為一般式 Point ret; double d = a * a + b * b; ret.x = (b * b * _Off.x - a * a * _Off.x - 2 * a * b * _Off.y - 2 * a * c) / d; ret.y = (a * a * _Off.y - b * b * _Off.y - 2 * a * b * _Off.x - 2 * b * c) / d; return ret; } //計算兩點的中垂線 static Line ppline(const Point &_a, const Point &_b) { Line ret; ret.s.x = (_a.x + _b.x) / 2; ret.s.y = (_a.y + _b.y) / 2; //一般式 ret.a = _b.x - _a.x; ret.b = _b.y - _a.y; ret.c = (_a.y - _b.y) * ret.s.y + (_a.x - _b.x) * ret.s.x; //兩點式 if(std::fabs(ret.a) > eps) { ret.e.y = 0.0; ret.e.x = - ret.c / ret.a; if(ret.e == ret. s) { ret.e.y = 1e10; ret.e.x = - (ret.c - ret.b * ret.e.y) / ret.a; } } else { ret.e.x = 0.0; ret.e.y = - ret.c / ret.b; if(ret.e == ret. s) { ret.e.x = 1e10; ret.e.y = - (ret.c - ret.a * ret.e.x) / ret.b; } } return ret; } //------------直線和直線(向量)------------- //直線向左邊平移t的距離 Line& moveLine(double t) { Point of; of=Point(-(e.y-s.y),e.x-s.x); double dis=sqrt(of.x*of.x+of.y*of.y); of.x=of.x*t/dis,of.y=of.y*t/dis; s=s+of,e=e+of; return *this; } //直線重合,參數:直線向量[_st],[_se] static bool equal(const Line &_st, const Line &_se) { return _st.lhas(_se.e) && _se.lhas(_se.s); } //直線平行,參數:直線向量[_st],[_se] static bool parallel(const Line &_st,const Line &_se) { return Point::sgn(_st / _se, 0) == 0; } //兩直線(線段)交點,參數:直線向量[_st],[_se],交點 //返回-1代表平行,0代表重合,1代表相交 static bool crossLPt(const Line &_st,const Line &_se,Point &ret) { if(Line::parallel(_st,_se)) { if(Line::equal(_st,_se)) return 0; return -1; } ret = _st.s; double t = (Line(_st.s,_se.s)/_se)/(_st/_se); ret.x += (_st.e.x - _st.s.x) * t; ret.y += (_st.e.y - _st.s.y) * t; return 1; } //------------線段和直線(向量)---------- //線段和直線交 //參數:直線[_st],線段[_se] friend bool crossSL(const Line &_st,const Line &_se) { return Point::sgn((_st / _se.s) * (_st / _se.e) ,0) <= 0; } //------------線段和線段(向量)---------- //判斷線段是否相交(註意添加eps),參數:線段[_st],線段[_se] static bool isCrossSS(const Line &_st,const Line &_se) { //1.快速排斥試驗判斷以兩條線段為對角線的兩個矩形是否相交 //2.跨立試驗(等於0時端點重合) return max(_st.s.x, _st.e.x) >= min(_se.s.x, _se.e.x) && max(_se.s.x, _se.e.x) >= min(_st.s.x, _st.e.x) && max(_st.s.y, _st.e.y) >= min(_se.s.y, _se.e.y) && max(_se.s.y, _se.e.y) >= min(_st.s.y, _st.e.y) && Point::sgn((_st / Line(_st.s, _se.s)) * (_st / Line(_st.s, _se.e)), 0) <= 0 && Point::sgn((_se / Line(_se.s, _st.s)) * (_se / Line(_se.s, _st.e)), 0) <= 0; } }; Point ptsort; bool gcmp(const Point &ta,const Point &tb)/// 選取與最後一條確定邊夾角最小的點,即余弦值最大者 { double tmp=Point::xmult(ptsort,ta,tb); if(Point::sgn(tmp,0)==0) return Point::dis(ptsort,ta)<Point::dis(ptsort,tb); else if(tmp>0) return 1; return 0; } class Polygon { public: const static int maxpn = 5e4+7; Point pt[maxpn];//點(順時針或逆時針) int n;//點的個數 //求多邊形面積,多邊形內點必須順時針或逆時針 double area() const { double ans = 0.0; for(int i = 0; i < n; i ++) { int nt = (i + 1) % n; ans += pt[i].x * pt[nt].y - pt[nt].x * pt[i].y; } return fabs(ans / 2.0); } //求多邊形重心,多邊形內點必須順時針或逆時針 Point gravity() const { Point ans; ans.x = ans.y = 0.0; double area = 0.0; for(int i = 0; i < n; i ++) { int nt = (i + 1) % n; double tp = pt[i].x * pt[nt].y - pt[nt].x * pt[i].y; area += tp; ans.x += tp * (pt[i].x + pt[nt].x); ans.y += tp * (pt[i].y + pt[nt].y); } ans.x /= 3 * area; ans.y /= 3 * area; return ans; } //判斷點在凸多邊形內,參數:點[_Off] bool chas(const Point &_Off) const { double tp = 0, np; for(int i = 0; i < n; i ++) { np = Line(pt[i], pt[(i + 1) % n]) / _Off; if(tp * np < -eps) return false; tp = (fabs(np) > eps)?np: tp; } return true; } /** 卷包裹法求點集凸包,_p為輸入點集,_n為點的數量 **/ void ConvexClosure(Point _p[],int _n) { sort(_p,_p+_n); n=0; for(int i=0;i<_n;i++) { while(n>1&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<0) n--; pt[n++]=_p[i]; } int _key=n; for(int i=_n-2;i>=0;i--) { while(n>_key&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<0) n--; pt[n++]=_p[i]; } if(n>1) n--;//除去重復的點,該點已是凸包凸包起點 } /****** 尋找凸包的graham 掃描法********************/ /****** _p為輸入的點集,_n為點的數量****************/ /**使用時需把gmp函數放在Polygon類上面L,ine類下面,並且看情況修改pt[0]**/ void graham(Point _p[],int _n) { int cur=0; for(int i=1;i<_n;i++) if(_p[cur].y>_p[i].y || (Point::sgn(_p[cur].y,_p[i].y)==0 && _p[cur].x>_p[i].x)) cur=i; swap(_p[cur],_p[0]); n=0,pt[n++]=_p[0],ptsort=_p[0]; if(_n==1) return; sort(_p+1,_p+_n,gcmp); pt[n++]=_p[1],pt[n++]=_p[2]; for(int i=3;i<_n;i++) { while(n>1 && Point::sgn(Point::xmult(pt[n-2],pt[n-1],_p[i]),0)<0)// 當凸包退化成直線時需特別註意n n--; pt[n++]=_p[i]; } } }; Point pt[1200]; Polygon py; Line ln; int main(void) { int t,n,cs=1;cin>>t; while(t--) { int ff=1; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%lf%lf",&pt[i].x,&pt[i].y); if(n<6) puts("NO"); else { py.graham(pt,n); py.pt[py.n]=py.pt[0]; // for(int i=0;i<py.n;i++) // printf("==%.2f %.2f\n",py.pt[i].x,py.pt[i].y); for(int i=1;i<py.n-1&&ff;i++) if(Point::xmult(py.pt[i-1],py.pt[i+1],py.pt[i])!=0&&Point::xmult(py.pt[i],py.pt[i+2],py.pt[i+1])!=0) ff=0; if(ff) puts("YES"); else puts("NO"); } } return 0; }

poj1228 Grandpa's Estate