HDU3644 2010 Asia Regional Hangzhou Site Online Contest D A Chocolate Manufacturer's Problem
1、判断多边形有向边顺、逆时针:
取最右点p[i],p[i-1]->p[i]与p[i]->p[i+1]成右手关系则为逆时针。
2、判断点与简单多边形位置关系:
参考文献:王学军,沈连婠,朱绍源等.基于左边的点在简单多边形内的判别算法[J].机械工程师,2006,(2):53-54.
给定一个简单多边形,判别点u在多边形G内外的判断算法步骤:
Step1:过u作一水平射线;
Step1:过u作一水平射线;
Step2:求出射线与简单多边形G的交点;
Step3:若交点为0,则u在G外,结束;
Step4:若交点数大于0,求出与u点距离最近交点v;
Step5:找到交点v所在边的两顶点,记p1为p[i],p2为p[i+1];
Step6:判断交点v是否是顶点,若v与p[i]重合,则p1为p[i-1],若v与p[i+1]重合,则p2为p[i+2];
Step7:判断 vu 到 vp1 沿逆时针方向的角度是否小于 vp2到vp1沿逆时针方向的角度,若是,则u在G内,否则在外,结束。
3、模拟退火,在边上选采样点可避免直接采样到多边形外,其他方面在方法上没有特别之处。参数调整比较纠结,改天编译器变了或者HDU数据变了这代码就不一定能AC了。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<math.h> 5 #include<time.h> 6 const int maxn = 55; 7 const double eps = 1e-8; 8 const double pi = acos(-1.0); 9 int dcmp(double x) 10 { 11 if(x > eps) return 1; 12 return x < -eps ? -1 : 0; 13 } 14 inline double min(double a, double b) 15 {return a < b ? a : b;} 16 inline double max(double a, double b) 17 {return a > b ? a : b;} 18 inline double Sqr(double x) 19 {return x * x;} 20 /**************************************************/ 21 //点及关于点、线的操作 22 struct Point 23 { 24 double x, y; 25 Point(){x = y = 0;} 26 Point(double a, double b) 27 {x = a, y = b;} 28 inline Point operator-(const Point &b)const 29 {return Point(x - b.x, y - b.y);} 30 inline Point operator+(const Point &b)const 31 {return Point(x + b.x, y + b.y);} 32 inline Point operator-() 33 {return Point(-x, -y);} 34 inline Point operator*(const double &b)const 35 {return Point(x * b, y * b);} 36 inline double dot(const Point &b)const 37 {return x * b.x + y * b.y;} 38 inline double cross(const Point &b, const Point &c)const 39 {return (b.x - x) * (c.y - y) - (c.x - x) * (b.y - y);} 40 inline double Dis(const Point &b)const 41 {return sqrt((*this - b).dot(*this - b));} 42 inline bool operator<(const Point &b)const 43 { 44 if(!dcmp(x - b.x)) return y < b.y; 45 return x < b.x; 46 } 47 inline bool operator>(const Point &b)const 48 {return b < *this;} 49 inline bool operator==(const Point &b)const 50 {return !dcmp(x - b.x) && !dcmp(y - b.y);} 51 inline bool InLine(const Point &b, const Point &c)const 52 {return !dcmp(cross(b, c));} 53 inline bool OnSeg(const Point &b, const Point &c)const//包括端点 54 {return InLine(b, c) && (*this - c).dot(*this - b) < eps;} 55 inline bool InSeg(const Point &b, const Point &c)const//不包括端点 56 {return InLine(b, c) && (*this - c).dot(*this - b) < -eps;} 57 double ToSeg(const Point&, const Point&)const; 58 }; 59 /**************************************************/ 60 bool Parallel(const Point &a, const Point &b, const Point &c, const Point &d) 61 {return !dcmp(a.cross(b, a + d - c));} 62 Point LineCross(const Point &a, const Point &b, const Point &c, const Point &d) 63 { 64 double u = a.cross(b, c), v = b.cross(a, d); 65 return Point((c.x * v + d.x * u) / (u + v), (c.y * v + d.y * u) / (u + v)); 66 } 67 double Point::ToSeg(const Point &b, const Point &c)const 68 { 69 Point t(x + b.y - c.y, y + c.x - b.x); 70 if(cross(t, b) * cross(t, c) > eps) 71 return min(Dis(b), Dis(c)); 72 return Dis(LineCross(*this, t, b, c)); 73 } 74 bool SegCross(const Point &a, const Point &b, 75 const Point &c, const Point &d, Point &p)//包括端点 76 { 77 if(Parallel(a, b, c, d)) return false; 78 if(a.cross(b, c) * a.cross(b, d) <= 0 && c.cross(d, a) * c.cross(d, b) <= 0) 79 { 80 p = LineCross(a, b, c, d); 81 return true; 82 } 83 return false; 84 } 85 /**************************************************/ 86 Point p[maxn]; 87 int n; 88 double r; 89 /**************************************************/ 90 double AngCounterClock(double start, double end) 91 {return end - start + (end > start - eps ? 2 * pi : 0);} 92 bool InSimplePolygon(Point u, Point p[], int n/*double neg_inf*/) 93 //判断点在简单多边形内,不包括边界,多边形点为逆时针 94 { 95 double neg_inf = -1e20, angvu, angvp1, angvp2; 96 Point v(0, u.y), p1, p2, tmp; 97 int i, id;//距离u最近交点对应线段起始点id 98 bool flag = false; 99 p[n] = p[0]; 100 //设u->v为水平负向射线 101 /* for(i = 0, neg_inf = 0; i < n; ++ i) neg_inf = min(neg_inf, p[i].x); 102 neg_inf -= 100.0; */ 103 v.x = neg_inf; 104 for(i = 0; i < n; ++ i) 105 { 106 if(u.OnSeg(p[i], p[i + 1])) return false; 107 if(!SegCross(u, v, p[i], p[i + 1], tmp)) continue; 108 flag = true; 109 if(tmp.x - v.x > eps) v.x = tmp.x, id = i; 110 } 111 if(!flag) return false; 112 p1 = v == p[id] ? p[(id + n - 1) % n] : p[id]; 113 p2 = v == p[id + 1] ? p[(id + 2) % n] : p[id + 1]; 114 angvu = atan2(u.y - v.y, u.x - v.x); 115 angvp1 = atan2(p1.y - v.y, p1.x - v.x); 116 angvp2 = atan2(p2.y - v.y, p2.x - v.x); 117 return AngCounterClock(angvu, angvp1) < 118 AngCounterClock(angvp2, angvp1) - eps; 119 } 120 /**************************************************/ 121 double PtoPolygon(Point u, Point p[], int n)//点到多边形边、点的最近距离 122 { 123 int i; 124 double res = 1e120; 125 p[n] = p[0]; 126 for(i = 0; i < n; ++ i) 127 res = min(res, u.ToSeg(p[i], p[i + 1])); 128 return res; 129 } 130 /**************************************************/ 131 //模拟退火 132 const int maxsam = 20; 133 const int pacelen = 5; 134 const double depace = 0.57; 135 const double endeps = 1e-3; 136 Point sam[maxsam]; 137 double mindis[maxsam]; 138 bool SA(double r) 139 { 140 int i, j, samnum; 141 Point tmp; 142 double tmpdis; 143 double maxx = -1e30, maxy = -1e30, minx = 1e30, miny = 1e30; 144 for(i = 0; i < n; ++ i) 145 { 146 maxx = max(p[i].x, maxx); 147 maxy = max(p[i].y, maxy); 148 minx = min(p[i].x, minx); 149 miny = min(p[i].y, miny); 150 } 151 samnum = maxsam < n ? maxsam : n; 152 double pace = sqrt(Sqr(maxx - minx) + Sqr(maxy - miny)); 153 for(i = 0; i < samnum; ++ i)//随机选边,随机比例选择边上的点作为采样点 154 { 155 j = rand() % n; 156 sam[i] = p[j] + (p[j + 1] - p[j]) * (rand() / 32767.0); 157 mindis[i] = 0; 158 } 159 for(; pace > endeps; pace *= depace) 160 for(i = 0; i < samnum; ++ i) 161 for(j = 0; j < pacelen; ++ j) 162 { 163 tmp.x = sam[i].x + cos((double)rand()) * pace; 164 tmp.y = sam[i].y + cos((double)rand()) * pace; 165 if(!InSimplePolygon(tmp, p, n)) continue; 166 tmpdis = PtoPolygon(tmp, p, n); 167 if(tmpdis > r - endeps) return true; 168 if(tmpdis > mindis[i]) 169 { 170 sam[i] = tmp; 171 mindis[i] = tmpdis; 172 } 173 } 174 return false; 175 } 176 /**************************************************/ 177 void MakeCounterClock(Point p[], int n)//顺时针则反转多边形的有向边 178 { 179 int i, id = 0; 180 p[n] = p[0]; 181 for(i = 0; i < n; ++ i) if(p[i].x > p[id].x) id = i; 182 if(p[id].cross(p[id + 1], p[(id + n - 1) % n]) > eps) return; 183 Point tmp; 184 for(i = n - 1 >> 1; i >= 0; -- i) 185 tmp = p[i], p[i] = p[n - i - 1], p[n - i - 1] = tmp; 186 } 187 int main() 188 { 189 srand(419); 190 while(scanf("%d", &n) && n) 191 { 192 for(int i = 0; i < n; ++ i) 193 scanf("%lf%lf", &p[i].x, &p[i].y); 194 MakeCounterClock(p, n); 195 scanf("%lf", &r); 196 printf(SA(r) ? "Yes\n" : "No\n"); 197 } 198 return 0; 199 }