LA 3263 好看的一笔画
平面上有一个包含n个顶点的一笔画,让求闭合曲线形成的图形将整个平面分成了几部分。
解题思路:
1,首先这题要转换思路利用到欧拉定理
欧拉定理:设平面图的顶点数、边数、和面数分别为V、E、F则V+F-E=2
那么面数为F = E+2-V
2,理解定理之后接下来的处理就是,记录相交生成的交点,删除重复的交点
3,最后就是求出有多少条线段、如果一个点出现在一个线段上(除了端点)、那么就多加一条边
4,用了《训练指南》上的模版,感觉这样写非常好
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 310 ;
struct Point{
double x,y ;
Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector ;
Point P[maxn] ;
Point V[maxn*maxn] ;
bool operator < (const Point& a, const Point& b) {///排序:x自然排序,y自然排序
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
Vector operator + (Vector A,Vector B){return Vector(A.x+B.x,A.y+B.y);}
Vector operator - (Point A,Point B){return Vector(A.x-B.x,A.y-B.y);}
Vector operator * (Vector A,double p){return Vector(A.x*p,A.y*p);}
double Cross(Vector A,Vector B){return A.x*B.y - A.y*B.x ;}
double Dot(Vector A,Vector B){return A.x*B.x + A.y*B.y ;}
const double eps = 1e-10 ;
int dcmp(double x){
if(fabs(x)<eps)return 0;
else return x<0?-1:1 ;
}
bool operator == (const Point& a, const Point &b) {///这个函数是unique函数中重载了==符号
return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2){
double c1 = Cross(a2-a1,b1-a1) ;
double c2 = Cross(a2-a1,b2-a1) ;
double c3 = Cross(b2-b1,a1-b1) ;
double c4 = Cross(b2-b1,a2-b1) ;
return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0 ;
}
bool OnSegment(Point p,Point a1,Point a2){return dcmp(Cross(a1-p,a2-p))==0&&dcmp(Dot(a1-p,a2-p))<0 ;}
Point GetLineIntersection(Point P,Vector v,Point Q,Vector w){
Vector u = P-Q ;
double t = Cross(w,u)/Cross(v,w) ;
return P+v*t ;
}
int main(){
int cas = 1 ;
int n ;
while(~scanf("%d",&n),n){
for(int i=0;i<n;i++){
scanf("%lf%lf",&P[i].x,&P[i].y);
V[i] = P[i] ;
}
n-- ;///最后一个点和第一个点相同
int c = n ;
int e = n ;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(SegmentProperIntersection(P[i],P[i+1],P[j],P[j+1])){///判断任意两条线段是否相交
V[c++] = GetLineIntersection(P[i],P[i+1]-P[i],P[j],P[j+1]-P[j]);///如果相交了就获得一个交点
}
}
}
sort(V,V+c) ;///对点进行排序
c = unique(V,V+c)-V ;///除去相同的点,返回值是不重复的元素最后一个元素的地址
for(int i=0;i<c;i++){///对于一个点,如果他在另外的一条线段上出现,那么这条线段就相当于被分为两段
for(int j=0;j<n;j++){
if(OnSegment(V[i],P[j],P[j+1])){
e++ ;
}
}
}
printf("Case %d: There are %d pieces.\n",cas++ ,e+2-c);
}
return 0;
}