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; //角度相等时,尾节点大于头节点
}

posted on 2012-09-06 11:43  男神发量  阅读(283)  评论(3编辑  收藏  举报