Find the Border UVALive - 3218(PSGL)
Find the Border
PSGL
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define pb push_back 4 const double eps = 1e-8; 5 const int inf = 0x3f3f3f3f; 6 7 int dcmp(double x) { 8 if(fabs(x) < eps) return 0; 9 return x < 0 ? -1 : 1; 10 } 11 12 struct Point { 13 double x,y; 14 Point (double x=0, double y=0): x(x), y(y) {} 15 }; 16 typedef Point Vector; 17 18 Vector operator + (Vector a, Vector b) { 19 return Vector(a.x+b.x, a.y+b.y); 20 } 21 Vector operator - (Point a, Point b) { 22 return Vector(a.x-b.x, a.y-b.y); 23 } 24 Vector operator * (Vector a, double p) { 25 return Vector(a.x*p, a.y*p); 26 } 27 Vector operator / (Vector a, double p) { 28 return Vector(a.x/p, a.y/p); 29 } 30 // 理论上这个“小于”运算符是错的,因为可能有三个点a, b, c, a和b很接近(即a<b好b<a都不成立),b和c很接近,但a和c不接近 31 // 所以使用这种“小于”运算符的前提是能排除上述情况 32 bool operator < (Point a, Point b) { 33 return dcmp(a.x-b.x)<0 || (dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)<0); 34 } 35 bool operator == (Point a, Point b) { 36 return dcmp(a.x-b.x)==0 && dcmp(a.y-b.y)==0; 37 } 38 double Dot(Vector a, Vector b) { 39 return a.x*b.x + a.y*b.y; 40 } 41 double Cross(Vector a, Vector b) { 42 return a.x*b.y - a.y*b.x; 43 } 44 double Length(Vector a) { 45 return sqrt(Dot(a,a)); 46 } 47 48 typedef vector<Point> Poly; 49 50 bool SegProIn(Point a1, Point b1, Point a2, Point b2) { 51 double c1 = Cross(b1-a1, b2-a1), c2 = Cross(b1-a1,a2-a1); 52 double c3 = Cross(b2-a2, a1-a2), c4 = Cross(b2-a2, b1-a2); 53 return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0; 54 } 55 Point GetLineIn(Point p, Vector v, Point q, Vector w) { 56 Vector u = p-q; 57 double t = Cross(w,u) / Cross(v,w); 58 return p+v*t; 59 } 60 bool OnSeg(Point p, Point a, Point b) { 61 return dcmp(Cross(a-p, b-p))==0 && dcmp(Dot(a-p, b-p))<0; 62 } 63 // 多边形的有向面积 64 double GetArea(Poly p) { 65 double area = 0; 66 int n = p.size(); 67 for(int i = 1; i < n-1; i++) area += Cross(p[i]-p[0], p[(i+1)%n]-p[0]); 68 return area/2; 69 } 70 71 struct Edge{ 72 int u, v; 73 double ang; 74 Edge(int u=0, int v=0, double ang=0): u(u), v(v), ang(ang){} 75 }; 76 const int maxe = 10000+10; // 最大边数 77 78 // 平面直线图(PSGL)实现 79 struct PSGL{ 80 int n, m, face_cnt; 81 double x[maxe], y[maxe]; 82 vector<Edge> edges; 83 vector<int> G[maxe]; 84 int vis[maxe<<1]; // 每条边是否已经访问过 85 int left[maxe<<1]; // 左面的编号 86 int pre[maxe<<1]; // 相同起点的上一条边(即顺时针旋转碰到的下一条边)的编号 87 88 vector<Poly> faces; 89 double area[maxe]; 90 91 void init(int n) { 92 this->n = n; 93 for(int i = 0; i < n; i++) G[i].clear(); 94 edges.clear(); 95 faces.clear(); 96 } 97 double getAngle(int u,int v) { 98 return atan2(y[v]-y[u], x[v]-x[u]); 99 } 100 void add(int u, int v) { 101 edges.pb(Edge(u,v,getAngle(u,v))); 102 edges.pb(Edge(v,u,getAngle(v,u))); 103 m = edges.size(); 104 G[u].pb(m-2); 105 G[v].pb(m-1); 106 } 107 //找出faces并计算面积 108 void build(){ 109 for(int u = 0; u < n; u++) { 110 int d = G[u].size(); 111 for(int i = 0; i < d; i++) { 112 for(int j = i+1; j < d; j++) { 113 if(edges[G[u][i]].ang > edges[G[u][j]].ang) swap(G[u][i],G[u][j]); 114 } 115 } 116 for(int i = 0; i < d; i++) pre[G[u][(i+1)%d]] = G[u][i]; 117 } 118 memset(vis, 0, sizeof(vis)); 119 face_cnt = 0; 120 for(int u = 0; u < n; u++) { 121 for(int i = 0; i < G[u].size(); i++){ 122 int e = G[u][i]; 123 if(!vis[e]){ //逆时针找圈 124 face_cnt++; 125 Poly p; 126 while(1){ 127 vis[e] = 1; 128 left[e] = face_cnt; 129 int from = edges[e].u; 130 p.pb(Point(x[from], y[from])); 131 e = pre[e^1]; 132 if(e == G[u][i]) break; 133 } 134 faces.pb(p); 135 } 136 } 137 } 138 for(int i = 0; i < faces.size(); i++) { 139 area[i] = GetArea(faces[i]); 140 141 } 142 } 143 144 }; 145 146 PSGL g; 147 148 const int maxv = 110; 149 int n, c; 150 151 Point p[maxv]; 152 Point V[maxv*maxv/2+maxv]; 153 154 int ID(Point p) { 155 return lower_bound(V, V+c, p) - V; 156 } 157 //假定poly没有相邻点重合的情况,只需要删除三点共线的情况 158 Poly simplify(const Poly& p) { 159 Poly temp; 160 int n = p.size(); 161 for(int i = 0; i < n; i++) { 162 Point a = p[i]; 163 Point b = p[(i+1)%n]; 164 Point c = p[(i+2)%n]; 165 if(dcmp(Cross(a-b, c-b)) != 0) temp.pb(b); 166 } 167 return temp; 168 } 169 170 void build_graph(){ 171 c = n; 172 for(int i = 0; i < n; i++) V[i] = p[i]; 173 vector<double> dis[maxv]; // dist[i][j]是第i条线段上的第j个点离起点(P[i])的距离 174 for(int i = 0; i < n; i++) { 175 for(int j = i+1; j < n; j++) { 176 if(SegProIn(p[i], p[(i+1)%n], p[j], p[(j+1)%n])) { 177 Point xy = GetLineIn(p[i], p[(i+1)%n]-p[i], p[j], p[(j+1)%n]-p[j]); 178 V[c++] = xy; 179 dis[i].pb(Length(xy - p[i])); 180 dis[j].pb(Length(xy - p[j])); 181 } 182 } 183 } 184 // 为了保证“很接近的点”被看作同一个,这里使用了sort+unique的方法 185 // 必须使用前面提到的“理论上是错误”的小于运算符,否则不能保证“很接近的点”在排序后连续排列 186 // 另一个常见的处理方式是把坐标扩大很多倍(比如100000倍),然后四舍五入变成整点(计算完毕后再还原),用少许的精度损失换来鲁棒性和速度。 187 sort(V, V+c); 188 c = unique(V, V+c) - V; 189 190 g.init(c); //c是平面图的点数 191 for(int i = 0; i < c; i++) { 192 g.x[i] = V[i].x; 193 g.y[i] = V[i].y; 194 } 195 for(int i = 0; i < n; i++) { 196 Vector v = p[(i+1)%n] - p[i]; 197 double len = Length(v); 198 dis[i].pb(0); 199 dis[i].pb(len); 200 sort(dis[i].begin(), dis[i].end()); 201 int sz = dis[i].size(); 202 for(int j = 1; j < sz; j++) { 203 Point a = p[i] + v*(dis[i][j-1]/len); 204 Point b = p[i] + v*(dis[i][j]/len); 205 if(a == b) continue; 206 g.add(ID(a), ID(b)); 207 } 208 } 209 g.build(); 210 211 Poly po; 212 for(int i = 0; i < g.faces.size(); i++) { 213 if(g.area[i] < 0) { // 对于连通图,惟一一个面积小于零的面是无限面 214 po = g.faces[i]; 215 reverse(po.begin(),po.end()); // 对于内部区域来说,无限面多边形的各个顶点是顺时针的 216 po = simplify(po); // 无限面多边形上可能会有相邻共线点 217 break; 218 } 219 } 220 int m = po.size(); 221 printf("%d\n", m); 222 int s = 0; 223 for(int i = 0; i < m; i++) if(po[i] < po[s]) s = i; 224 for(int i = s; i < m; i++) printf("%.4lf %.4lf\n", po[i].x, po[i].y); 225 for(int i = 0; i < s; i++) printf("%.4lf %.4lf\n", po[i].x, po[i].y); 226 } 227 228 229 int main(){ 230 while(scanf("%d", &n)!=EOF) { 231 int x,y; 232 for(int i = 0; i < n; i++) { 233 scanf("%d %d", &x, &y); 234 p[i] = Point(x,y); 235 } 236 build_graph(); 237 } 238 return 0; 239 }