码迷,mamicode.com
首页 > 其他好文 > 详细

uvalive 3218 Find the Border

时间:2015-10-20 01:16:47      阅读:236      评论:0      收藏:0      [点我收藏+]

标签:

题意:一条封闭折线将平面分成了若干个区域,按顺序给出折线各点的坐标,要求输出封闭折线的轮廓。
题解:用类似卷包裹的算法,先确定一个一定会被选中的点(x坐标最小,y坐标最小)作为起点,然后把可能是下一个极点(凸包顶点)的点都存起来,下一个极点有可能是当前点所在线段的前一个点和后一个点或当前点所在线段和其他线段的有交点的线段的起点和终点。
找出最右侧的点(用角度判断)和当前点的连线是否和其他线段有交点,如果有就找最近的交点当做答案的下一个点,如果没有最右侧的点就是下一个点。最后转回起点结束。

技术分享
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cmath>
  4 #include <algorithm>
  5 using namespace std;
  6 const double PI = acos(-1);
  7 const double eps = 1e-9;
  8 struct Point
  9 {
 10     double x, y;
 11     Point(double x = 0, double y = 0): x(x), y(y) {}
 12 };
 13 typedef Point Vector;
 14 double dcmp(double x)
 15 {
 16     if (fabs(x) < eps)
 17         return 0;
 18     return x < 0 ? -1 : 1;
 19 }
 20 Vector operator + (const Point& A, const Point& B)
 21 {
 22     return Vector(A.x + B.x, A.y + B.y);
 23 }
 24 Vector operator - (const Point& A, const Point& B)
 25 {
 26     return Vector(A.x - B.x, A.y - B.y);
 27 }
 28 Vector operator * (const Point& A, double a)
 29 {
 30     return Vector(A.x * a, A.y * a);
 31 }
 32 Vector operator / (const Point& A, double a)
 33 {
 34     return Vector(A.x / a, A.y / a);
 35 }
 36 double Cross(const Vector& A, const Vector& B)
 37 {
 38     return A.x * B.y - A.y * B.x;
 39 }
 40 double Dot(const Vector& A, const Vector& B)
 41 {
 42     return A.x * B.x + A.y * B.y;
 43 }
 44 double Length(const Vector& A)
 45 {
 46     return sqrt(Dot(A, A));
 47 }
 48 bool operator < (const Point& A, const Point& B)
 49 {
 50     return A.x < B.x || (A.x == B.x && A.y < B.y);
 51 }
 52 bool operator == (const Point& A, const Point& B)
 53 {
 54     return A.x == B.x && A.y == B.y;
 55 }
 56 Point GetLineIntersection(Point P, Point v, Point Q, Point w)
 57 {
 58     Point u = P - Q;
 59     double t = Cross(w, u) / Cross(v, w);
 60     return P + v * t;
 61 }
 62 bool SegmentProperIntersection(const Point& a1, const Point& a2, const Point& b1, const Point& b2)
 63 {
 64     double c1 = Cross(a2 - a1, b1 - a1);
 65     double c2 = Cross(a2 - a1, b2 - a1);
 66     double c3 = Cross(b2 - b1, a1 - b1);
 67     double c4 = Cross(b2 - b1, a2 - b1);
 68     return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
 69 }
 70 bool OnSegment(const Point& p, const Point& a1, const Point& a2)
 71 {
 72     return dcmp(Cross(a1 - p, a2 - p)) == 0 && dcmp(Dot(a1 - p, a2 - p)) < 0;
 73 }
 74 
 75 const int N = 10005;
 76 const int M = 205;
 77 Point P[M], res[N], temp[M];
 78 double ang[M], s;
 79 int n, cnt;
 80 
 81 void add(Point a, Point b)
 82 {
 83     temp[cnt] = a;
 84     ang[cnt] = atan2(temp[cnt].y - b.y, temp[cnt].x - b.x) - s;
 85     while (dcmp(ang[cnt]) <= 0)
 86         ang[cnt] += 2 * PI;
 87     cnt++;
 88 }
 89 
 90 int main()
 91 {
 92     while (scanf("%d", &n) == 1)
 93     {
 94         int minid = 0;
 95         for (int i = 0; i < n; i++)
 96         {
 97             scanf("%lf%lf", &P[i].x, &P[i].y);
 98             if (P[i] < P[minid])
 99                 minid = i;
100         }
101         res[0] = P[minid];
102         int num = 1;
103         s = -PI;
104         while (1)
105         {
106             cnt = 0;
107             for (int i = 0; i < n; i++)
108             {
109                 if (res[num - 1] == P[i])
110                 {
111                     add(P[(i + 1) % n], res[num - 1]);
112                     add(P[(i + n - 1) % n], res[num - 1]);
113                     break;
114                 }
115             }
116             for (int i = 0; i < n; i++)
117             {
118                 if (OnSegment(res[num - 1], P[i], P[(i + 1) % n]))
119                 {
120                     add(P[(i + 1) % n], res[num - 1]);
121                     add(P[i], res[num - 1]);
122                 }
123             }
124             int id = 0;
125             for (int i = 0; i < cnt; i++)
126                 if (ang[i] < ang[id])
127                     id = i;
128             double minlen = 1e9;
129             Point RP = temp[id], its;
130             for (int i = 0; i < n; i++)
131             {
132                 if (SegmentProperIntersection(temp[id], res[num - 1], P[i], P[(i + 1) % n]))
133                 {
134                     its = GetLineIntersection(temp[id], temp[id] - res[num - 1], P[i], P[i] - P[(i + 1) % n]);
135                     if (Length(its - res[num - 1]) < minlen)
136                     {
137                         minlen = Length(its - res[num - 1]);
138                         RP = its;
139                     }
140                 }
141             }
142             res[num] = RP;
143             s = atan2(res[num - 1].y - res[num].y, res[num - 1].x - res[num].x);
144             num++;
145             if (res[num - 1] == res[0])
146                 break;
147         }
148         printf("%d\n", num - 1);
149         for (int i = 0; i < num - 1; i++)
150             printf("%.4lf %.4lf\n", res[i].x, res[i].y);
151     }
152     return 0;
153 }
View Code

后一种解法是用PSLG的外轮廓。

技术分享
  1 #include<cstdio>
  2 #include<vector>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cassert>
  7 using namespace std;
  8 
  9 const double eps = 1e-8;
 10 double dcmp(double x)
 11 {
 12     if(fabs(x) < eps) return 0;
 13     else return x < 0 ? -1 : 1;
 14 }
 15 
 16 struct Point
 17 {
 18     double x, y;
 19     Point(double x=0, double y=0):x(x),y(y) { }
 20 };
 21 
 22 typedef Point Vector;
 23 
 24 Vector operator + (Vector A, Vector B)
 25 {
 26     return Vector(A.x+B.x, A.y+B.y);
 27 }
 28 
 29 Vector operator - (Point A, Point B)
 30 {
 31     return Vector(A.x-B.x, A.y-B.y);
 32 }
 33 
 34 Vector operator * (Vector A, double p)
 35 {
 36     return Vector(A.x*p, A.y*p);
 37 }
 38 
 39 // 理论上这个“小于”运算符是错的,因为可能有三个点a, b, c, a和b很接近(即a<b好b<a都不成立),b和c很接近,但a和c不接近
 40 // 所以使用这种“小于”运算符的前提是能排除上述情况
 41 bool operator < (const Point& a, const Point& b)
 42 {
 43     return dcmp(a.x - b.x) < 0 || (dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) < 0);
 44 }
 45 
 46 bool operator == (const Point& a, const Point &b)
 47 {
 48     return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
 49 }
 50 
 51 double Dot(Vector A, Vector B)
 52 {
 53     return A.x*B.x + A.y*B.y;
 54 }
 55 double Cross(Vector A, Vector B)
 56 {
 57     return A.x*B.y - A.y*B.x;
 58 }
 59 double Length(Vector A)
 60 {
 61     return sqrt(Dot(A, A));
 62 }
 63 
 64 typedef vector<Point> Polygon;
 65 
 66 Point GetLineIntersection(const Point& P, const Vector& v, const Point& Q, const Vector& w)
 67 {
 68     Vector u = P-Q;
 69     double t = Cross(w, u) / Cross(v, w);
 70     return P+v*t;
 71 }
 72 
 73 bool SegmentProperIntersection(const Point& a1, const Point& a2, const Point& b1, const Point& b2)
 74 {
 75     double c1 = Cross(a2-a1,b1-a1), c2 = Cross(a2-a1,b2-a1),
 76            c3 = Cross(b2-b1,a1-b1), c4=Cross(b2-b1,a2-b1);
 77     return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0;
 78 }
 79 
 80 bool OnSegment(Point p, Point a1, Point a2)
 81 {
 82     return dcmp(Cross(a1-p, a2-p)) == 0 && dcmp(Dot(a1-p, a2-p)) < 0;
 83 }
 84 
 85 // 多边形的有向面积
 86 double PolygonArea(Polygon poly)
 87 {
 88     double area = 0;
 89     int n = poly.size();
 90     for(int i = 1; i < n-1; i++)
 91         area += Cross(poly[i]-poly[0], poly[(i+1)%n]-poly[0]);
 92     return area/2;
 93 }
 94 
 95 struct Edge
 96 {
 97     int from, to; // 起点,终点,左边的面编号
 98     double ang;
 99 };
100 
101 const int maxn = 10000 + 10; // 最大边数
102 
103 // 平面直线图(PSGL)实现
104 struct PSLG
105 {
106     int n, m, face_cnt;
107     double x[maxn], y[maxn];
108     vector<Edge> edges;
109     vector<int> G[maxn];
110     int vis[maxn*2];  // 每条边是否已经访问过
111     int left[maxn*2]; // 左面的编号
112     int prev[maxn*2]; // 相同起点的上一条边(即顺时针旋转碰到的下一条边)的编号
113 
114     vector<Polygon> faces;
115     double area[maxn]; // 每个polygon的面积
116 
117     void init(int n)
118     {
119         this->n = n;
120         for(int i = 0; i < n; i++) G[i].clear();
121         edges.clear();
122         faces.clear();
123     }
124 
125     // 有向线段from->to的极角
126     double getAngle(int from, int to)
127     {
128         return atan2(y[to]-y[from], x[to]-x[from]);
129     }
130 
131     void AddEdge(int from, int to)
132     {
133         edges.push_back((Edge)
134         {
135             from, to, getAngle(from, to)
136         });
137         edges.push_back((Edge)
138         {
139             to, from, getAngle(to, from)
140         });
141         m = edges.size();
142         G[from].push_back(m-2);
143         G[to].push_back(m-1);
144     }
145 
146     // 找出faces并计算面积
147     void Build()
148     {
149         for(int u = 0; u < n; u++)
150         {
151             // 给从u出发的各条边按极角排序
152             int d = G[u].size();
153             for(int i = 0; i < d; i++)
154                 for(int j = i+1; j < d; j++) // 这里偷个懒,假设从每个点出发的线段不会太多
155                     if(edges[G[u][i]].ang > edges[G[u][j]].ang) swap(G[u][i], G[u][j]);
156             for(int i = 0; i < d; i++)
157                 prev[G[u][(i+1)%d]] = G[u][i];
158         }
159 
160         memset(vis, 0, sizeof(vis));
161         face_cnt = 0;
162         for(int u = 0; u < n; u++)
163             for(int i = 0; i < G[u].size(); i++)
164             {
165                 int e = G[u][i];
166                 if(!vis[e])   // 逆时针找圈
167                 {
168                     face_cnt++;
169                     Polygon poly;
170                     for(;;)
171                     {
172                         vis[e] = 1;
173                         left[e] = face_cnt;
174                         int from = edges[e].from;
175                         poly.push_back(Point(x[from], y[from]));
176                         e = prev[e^1];
177                         if(e == G[u][i]) break;
178                         assert(vis[e] == 0);
179                     }
180                     faces.push_back(poly);
181                 }
182             }
183 
184         for(int i = 0; i < faces.size(); i++)
185         {
186             area[i] = PolygonArea(faces[i]);
187         }
188     }
189 };
190 
191 PSLG g;
192 
193 const int maxp = 100 + 5;
194 int n, c;
195 Point P[maxp];
196 
197 Point V[maxp*(maxp-1)/2+maxp];
198 
199 // 在V数组里找到点p
200 int ID(Point p)
201 {
202     return lower_bound(V, V+c, p) - V;
203 }
204 
205 // 假定poly没有相邻点重合的情况,只需要删除三点共线的情况
206 Polygon simplify(const Polygon& poly)
207 {
208     Polygon ans;
209     int n = poly.size();
210     for(int i = 0; i < n; i++)
211     {
212         Point a = poly[i];
213         Point b = poly[(i+1)%n];
214         Point c = poly[(i+2)%n];
215         if(dcmp(Cross(a-b, c-b)) != 0) ans.push_back(b);
216     }
217     return ans;
218 }
219 
220 void build_graph()
221 {
222     c = n;
223     for(int i = 0; i < n; i++)
224         V[i] = P[i];
225 
226     vector<double> dist[maxp]; // dist[i][j]是第i条线段上的第j个点离起点(P[i])的距离
227     for(int i = 0; i < n; i++)
228         for(int j = i+1; j < n; j++)
229             if(SegmentProperIntersection(P[i], P[(i+1)%n], P[j], P[(j+1)%n]))
230             {
231                 Point p = GetLineIntersection(P[i], P[(i+1)%n]-P[i], P[j], P[(j+1)%n]-P[j]);
232                 V[c++] = p;
233                 dist[i].push_back(Length(p - P[i]));
234                 dist[j].push_back(Length(p - P[j]));
235             }
236 
237     // 为了保证“很接近的点”被看作同一个,这里使用了sort+unique的方法
238     // 必须使用前面提到的“理论上是错误”的小于运算符,否则不能保证“很接近的点”在排序后连续排列
239     // 另一个常见的处理方式是把坐标扩大很多倍(比如100000倍),然后四舍五入变成整点(计算完毕后再还原),用少许的精度损失换来鲁棒性和速度。
240     sort(V, V+c);
241     c = unique(V, V+c) - V;
242 
243     g.init(c); // c是平面图的点数
244     for(int i = 0; i < c; i++)
245     {
246         g.x[i] = V[i].x;
247         g.y[i] = V[i].y;
248     }
249     for(int i = 0; i < n; i++)
250     {
251         Vector v = P[(i+1)%n] - P[i];
252         double len = Length(v);
253         dist[i].push_back(0);
254         dist[i].push_back(len);
255         sort(dist[i].begin(), dist[i].end());
256         int sz = dist[i].size();
257         for(int j = 1; j < sz; j++)
258         {
259             Point a = P[i] + v * (dist[i][j-1] / len);
260             Point b = P[i] + v * (dist[i][j] / len);
261             if(a == b) continue;
262             g.AddEdge(ID(a), ID(b));
263         }
264     }
265 
266     g.Build();
267 
268     Polygon poly;
269     for(int i = 0; i < g.faces.size(); i++)
270         if(g.area[i] < 0)   // 对于连通图,惟一一个面积小于零的面是无限面
271         {
272             poly = g.faces[i];
273             reverse(poly.begin(), poly.end()); // 对于内部区域来说,无限面多边形的各个顶点是顺时针的
274             poly = simplify(poly); // 无限面多边形上可能会有相邻共线点
275             break;
276         }
277 
278     int m = poly.size();
279     printf("%d\n", m);
280 
281     // 挑选坐标最小的点作为输出的起点
282     int start = 0;
283     for(int i = 0; i < m; i++)
284         if(poly[i] < poly[start]) start = i;
285     for(int i = start; i < m; i++)
286         printf("%.4lf %.4lf\n", poly[i].x, poly[i].y);
287     for(int i = 0; i < start; i++)
288         printf("%.4lf %.4lf\n", poly[i].x, poly[i].y);
289 }
290 
291 int main()
292 {
293     while(scanf("%d", &n) == 1 && n)
294     {
295         for(int i = 0; i < n; i++)
296         {
297             int x, y;
298             scanf("%d%d", &x, &y);
299             P[i] = Point(x, y);
300         }
301         build_graph();
302     }
303     return 0;
304 }
View Code

 

uvalive 3218 Find the Border

标签:

原文地址:http://www.cnblogs.com/ITUPC/p/4893463.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!