uva11355 Cool Points 极角排序
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 const int inf = 5020; 9 const double pai2 = 2*acos(-1); 10 struct point{ 11 int x,y,at; 12 bool tail; 13 point(int a=0,int b=0,bool t=false){ x=a,y=b; tail=t; 14 if(x > 0){ 15 if(y > 0 || (y>=0 && x <inf)) at =1; 16 else at = 4; 17 } 18 else if(y > 0) at = 2; 19 else at = 3; 20 } 21 friend bool operator<(const point &a,const point &b); 22 }; 23 bool operator<(const point &a,const point &b) 24 { 25 if(a.at != b.at) return a.at > b.at; 26 int t = a.x*b.y - b.x*a.y; 27 if(t != 0) return t < 0; 28 return a.tail&&!b.tail; 29 } 30 priority_queue<point> tank; 31 point A,B; 32 double sum; 33 void add_up() 34 { 35 //cout<<"A -> "<<A.x<<" "<<A.y<<" "<<A.tail<<" at "<<A.at<<endl; cout<<"B -> "<<B.x<<" "<<B.y<<" "<<B.tail<<" at "<<B.at<<endl; 36 long long int a2 = A.x*A.x+A.y*A.y, b2 = B.x*B.x+B.y*B.y, c2 = (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y); 37 double minus = a2+b2-c2; 38 double div = sqrt(double(a2*b2)); 39 div *= 2; 40 minus = acos(minus/div); 41 if(A.x*B.y <= A.y*B.x) sum += pai2 - minus; //判角度是否大于180度 42 else sum += minus; 43 //cout<<"sum = "<<sum<<endl; 44 } 45 int pxp(int &x1,int &y1,int &x2,int &y2) 46 { 47 int ans = x1*y2 - x2*y1; 48 if(ans < 0) 49 { swap(x1,x2); 50 swap(y1,y2); } 51 return ans; 52 } 53 void cross_x(int x1,int y1,int x2,int y2) 54 { 55 if(y1 < 0 && y2 > 0) 56 { tank.push(point(x1,y1)); 57 tank.push(point(inf,0,true)); 58 tank.push(point(1,0)); 59 tank.push(point(x2,y2,true)); } 60 else if(y1 < 0 && y2 == 0) 61 { tank.push(point(x1,y1)); 62 tank.push(point(inf,0,true)); } 63 else{ 64 tank.push(point(x1,y1)); 65 tank.push(point(x2,y2,true)); 66 } 67 } 68 int main() 69 { 70 int cases,n; 71 double r; 72 cin>>cases; 73 for(int ncas=1;ncas<=cases;ncas++) 74 { cin>>n>>r; 75 sum = 0; 76 int x1,y1,x2,y2; 77 while(n--) 78 { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 79 if(pxp(x1,y1,x2,y2) != 0) 80 cross_x(x1,y1,x2,y2); } 81 int cnt = 0; 82 point tem; 83 while(!tank.empty()){ 84 tem = tank.top(); 85 //cout<<"point -> "<<tem.x<<" "<<tem.y<<" "<<tem.tail<<" at "<<tem.at<<endl; 86 tank.pop(); 87 if(tem.tail) { cnt--; 88 if(cnt == 0) 89 { B = tem; 90 add_up(); } 91 } 92 else { 93 if(cnt == 0) A = tem; 94 cnt++; 95 } 96 } 97 sum = pai2 - sum; 98 sum *= 100; 99 sum /= pai2; 100 char CC = '%'; 101 printf("Case %d: %.2f%c\n",ncas,sum,CC); 102 } 103 system("pause"); 104 return 0; 105 }
题目大意:在一个直角坐标系中,给出n个线段两端点坐标,求以原点为中心,所有线段的两端与原点相连所围成的角度和sum。(PS:多个线段这样重合部分只计一次) 输出(1-sum/(2*pai)),格式见题目中样例输出。
思路:将所有点与原点相连形成一个向量,对于每条线段的两个端点,根据给出的结果可判别大小。并标明起点(到角较小的点)、终点(到角较大的点)。从小到大,利用余弦定理,求出线段的两端点与原点所构成的角度。需要注意的是,多个线段有可能会重合。为了简单、提高精度起见,需要“合并”线段。例如,若AB,与A'B'有重合部分,应取起点中与原点形成的向量的到角最小的那个为起点,取相应到角最大的为终点。(PS:对于多条线段重合都同样处理)另外两个需要注意的地方就是:一、若某个线段与X正半轴有交点(即可能导致判起点终点时出错)则需要拆分成两条线段;二、对于刚刚提到的线段“合并”处理有可能导致那样围城的角度大于180度,所以要加条件判断(利用叉积)。
极角排序,之前说到了用向量坐标(x,y)不确定地表示一下X正半轴到这个向量的到角大小。按照我上一篇博客里提到的方法,如果也有头尾节点的比较,未免有些麻烦。在此,我奉劝读者可以尝试一种新方法:先确定向量所在的象限,同一象限内再用叉积判断向量的到角大小!
看代码:
struct point{
int x,y,at; //坐标,所属象限
bool tail; //尾节点标志
point(int a=0,int b=0,bool t=false){ x=a,y=b; tail=t;
if(x > 0){
if(y > 0 || (y>=0 && x <inf)) at =1;
else at = 4; //如果X>=inf(某一较大正常数),且在X正半轴上,定义为360度。
}
else if(y > 0) at = 2;
else at = 3;
}
friend bool operator<(const point &a,const point &b);
};
bool operator<(const point &a,const point &b) //实际重载的是大于号
{
if(a.at != b.at) return a.at > b.at; //象限判大小
int t = a.x*b.y - b.x*a.y; //叉积判大小
if(t != 0) return t < 0;
return a.tail&&!b.tail; //角度相等时,尾节点大于头节点
}