1 /*LA3263计算几何+欧拉定理的应用+线段交判边
2 欧拉定理:顶点+边数-面数=2
3 思路:先找到枚举的范围,减少判断的集合,再筛选。
4 巧妙之处:线段间产生的点如果被夹在原先定点的连线上,则产生一条新的边
5 易错处:
6 1、给出的第一个点和最后一个点是重合的,所以最终有n-1个初始点
7 2、应该统一所有的点,在去重,因为新增点可能和给定点相同
8 3、结构体重载== 时注意精度处理
9 */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <math.h>
14 #include <ctype.h>
15 #include <string>
16 #include <iostream>
17 #include <sstream>
18 #include <vector>
19 #include <queue>
20 #include <stack>
21 #include <map>
22 #include <list>
23 #include <set>
24 #include <algorithm>
25 #define INF 0x3f3f3f3f
26 #define LL long long
27 #define eps 1e-7
28 using namespace std;
29
30 struct Point
31 {
32 double x,y;
33 Point(double x=0,double y=0):x(x),y(y){}
34 };
35 typedef Point Vector;
36 int dcmp(double x)
37 {
38 if(fabs(x) < eps)return 0;
39 else return x < 0 ? -1 : 1;
40 }
41 bool operator < (const Point &a, const Point &b)
42 {
43 return a.x < b.x || (a.x == b.x && a.y < b.y);
44 }
45 bool operator == (const Point& a, const Point &b)
46 {
47 return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
48 }
49
50 Vector operator-(Point A,Point B)//表示A指向B
51 {
52 return Vector(A.x-B.x,A.y-B.y);
53 }
54 Vector operator*(Vector A,double k)
55 {
56 return Vector(A.x*k,A.y*k);
57 }
58 Vector operator+(Point A,Point B)//表示A指向B
59 {
60 return Vector(B.x+A.x,B.y+A.y);
61 }
62
63 double Dot(Vector A,Vector B)
64 {
65 return A.x*B.x+A.y*B.y;
66 }
67 double Length(Vector A)
68 {
69 return sqrt(Dot(A,A));
70 }
71 double Angle(Vector A,Vector B)
72 {
73 return fabs(acos(Dot(A,B)/Length(A)/Length(B)));
74 }
75 double Cross(Vector A,Vector B) {return A.x*B.y-A.y*B.x;}
76 double Area(Point A,Point B,Point C)//三角形面积
77 {
78 return Cross(B-A,C-A)/2;
79 }
80 bool SegmentProperIntersection(Point a1, Point a2, Point b1, Point b2)
81 {
82 double c1 = Cross(a2-a1,b1-a1), c2 = Cross(a2-a1, b2-a1);
83 double c3 = Cross(b2-b1,a1-b1), c4 = Cross(b2-b1, a2-b1);
84 return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0;
85 }
86 Point GetLineIntersection(Point P, Vector v, Point Q, Vector w)
87 {
88 Vector u = P-Q;
89 double t = Cross(w, u) / Cross(v, w);
90 return P+v*t;//在精度要求极高的情况下,可以考虑自定义分数类
91 }
92 bool OnSegment(Point p, Point a1, Point a2)
93 {
94 return dcmp(Cross(a1-p, a2-p))==0 && dcmp(Dot(a1 - p ,a2 - p) < 0);//含端点就是<=0
95 }
96 int n,cas=0;
97 vector<Point>P;
98 vector<Point>np;
99 int main()
100 {//欧拉公式:顶点+边数-面数=2
101 while(cin>>n && n)
102 {
103 cas++;
104 P.clear();
105 np.clear();
106 for(int i=0;i<n;i++)
107 {
108 int x,y;
109 cin>>x>>y;
110 P.push_back(Point(x,y));
111 np.push_back(Point(x,y));//把给定点也加入的原因是:后来的连线也可能穿过给定的点。容易犯错!
112 }
113 n--;//这是一个坑,因为第一个和最后一个点相同
114 int v,e=n;//v是输入的顶点,e是在被分割前一定有n条边
115 for(int i=0;i<n;i++)
116 for(int j=i+1;j<n;j++)
117 {
118 if (SegmentProperIntersection(P[i],P[i+1],P[j],P[j+1]))//暴力枚举线段相交
119 {
120 np.push_back(GetLineIntersection(P[i],P[i+1]-P[i],P[j],P[j+1]-P[j]));
121 }
122 }
123 sort(np.begin(),np.end());
124 int cnt=unique(np.begin(),np.end())-np.begin();
125 v=cnt;
126 for(int i=0;i<cnt;i++)//枚举每个新的点是否分割一条原来的线段,实际上,多次枚举的原因是去除多线共点的问题,不然就可以直接用数量计算出来
127 {
128 for(int j=0;j<n;j++)//注意原先线段必然是连续的点构成
129 {
130 if ( OnSegment(np[i], P[j], P[j+1] ) ) e++;//点在线段上但不在端点上
131 }
132 }
133 printf("Case %d: There are %d pieces.\n",cas,e+2-v);
134 }
135 return 0;
136 }