题意:一条封闭折线将平面分成了若干个区域,按顺序给出折线各点的坐标,要求输出封闭折线的轮廓。
题解:用类似卷包裹的算法,先确定一个一定会被选中的点(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

转载于:https://www.cnblogs.com/ITUPC/p/4893463.html

uvalive 3218 Find the Border相关推荐

  1. DP UVALive 6506 Padovan Sequence

    题目传送门 /*题意:两行数字,相邻列一上一下,或者隔一列两行都可以,从左到右选择数字使和最大DP:状态转移方程:dp[i][j] = max (dp[i][j], dp[1-i][j-1] + a[ ...

  2. 巧用CSS的Border属性

    . 作者:冯永曜 来源:黄山村夫 制作过网页的人都有为画线而烦恼的经历,本文介绍的小技巧也许对你有所帮助.我们先来认识一下"Border"(画边框),它是CSS的一个属性,用它可以 ...

  3. CSS盒子模型(border、padding、margin、圆角边框、盒子阴影、文字阴影、新闻列表综合案例、新浪导航栏案例)

    1. 网页布局的本质 首先利用CSS设置好盒子的大小,然后摆放盒子的位置. 最后把网页元素比如文字图片等等,放入盒子里面. 以上两步 就是网页布局的本质 2. 盒子模型(Box Model) 盒子模型 ...

  4. R语言使用magick包的image_border函数和image_background函数自定义图像的边界和背景(Change image border and background)

    R语言使用magick包的image_border函数和image_background函数自定义图像的边界和背景(Change image border and background) 目录

  5. R语言ggplot2可视化分面图(faceting):自定义分面图可视化、ggplot2可视化分面图并移除分面图之间的边框线条(Remove Panel Border Lines in a facet

    R语言ggplot2可视化分面图(faceting):自定义分面图可视化.ggplot2可视化分面图并移除分面图之间的边框线条(Remove Panel Border Lines in a facet ...

  6. R语言ggplot2可视化设置数据点的大小、设置数据点的颜色、设置数据点边缘线条的厚度(point border thickness)实战

    R语言ggplot2可视化设置数据点的大小.设置数据点的颜色.设置数据点边缘线条的厚度(point border thickness)实战 目录

  7. java swing setborder_Swing编程边框(Border)的用法总结

    2. 下面列出程序的完整实现代码: Java示例: package Test; import java.awt.*; import javax.swing.*; import javax.swing. ...

  8. CSS魔法堂:重拾Border之——图片作边框

    前言  当CSS3推出border-radius属性时我们是那么欣喜若狂啊,一想到终于不用再添加额外元素来模拟圆角了,但发现border-radius还分水平半径和垂直半径,然后又发现border-t ...

  9. 怎么用css控制border成为三角形

    布局div,并命名id="tri",用CSS来控制div,在style里面,使用border属性对div进行控制, #tri{ width: 0px; height: 0px; b ...

最新文章

  1. python dicom图像分割_处理医疗影像的Python利器:PyDicom
  2. linux脚本参数获取时间戳,Linux系统date命令的参数及获取时间戳的方法
  3. python怎么导入时间-python 插入日期数据到Oracle
  4. 广域网智能流量调度—Vecloud
  5. 2019年Q3:全球超大规模数据中心数量增至504个
  6. vi的visual模式
  7. SharePoint 2010 中的BCS身份验证模式
  8. ssl提高组周二备考赛【2018.10.30】
  9. “程序员不会数据分析,有什么影响?”资深程序员:基本等于自废武功!
  10. 自助图书馆系统-Tkinter界面和openpyxl表格综合设计案例
  11. 2012年8月编程语言排行榜:C# 的航向在哪里
  12. 【手机跳板 多款软件测试】图文演示!
  13. hdfs配置文件(hdfs.site.xml)详解
  14. 服务器黑屏只显示鼠标是什么意思,电脑黑屏只显示鼠标怎么办
  15. 关于 __dirname和__filename介绍以及使用场景
  16. 计算机老年人操作使用方法,老年人操作电脑五注意
  17. 计算机毕业设计Java学校食堂管理(源码+系统+mysql数据库+lw文档)
  18. 如何成为更好的软件架构师?
  19. 自动控制原理知识点梳理——6.线性系统的校正方法
  20. linux下,批量复制文件名带空格的文件

热门文章

  1. RemoteFX原理简介
  2. three.ar.js_我们如何通过AR.js使产品吉祥物栩栩如生
  3. 查缺补漏 | Python自定义函数
  4. Python培训分享:Python内置标准异常及其解析
  5. Python中怎样改变集合之间的关系?
  6. Java虚拟机的内存空间有几种
  7. 共享程序集和强命名程序集(3):强命名程序集的一些作用
  8. 02、在层级未知情况下通过递归查找子物体
  9. java如何读写json文件
  10. C语言双链表遍历,插入,删除