poj4048

   高精度的极角计算是种很实用的算法。由于计算机采用64位的二进制来表示实数,不管再怎么样,总是会带来误差的。所以采用整数计算才能做到精确无误,当然毕竟角度值可以是任意实数,所以我说的整数计算,只是把角度值采用两个整数(类似于数学里一个向量)给映射出来(通过象限等的限制),准确的表达出角度的大小关系。代码中小于符号重载是核心,请读者仔细阅读。

     题目抽象大意:在一个二维平面坐标中,有n条线段(每个点用(x1,y1),(x2,y2)表示两个端点([-10000,10000]*[-10000,10000]范围内)),给定某个点(x0,y0),以这个点为起点,求以某个方向引出一条射线能交到的最大线段数。

     先不管这个题,我们来看另外一个情景:某个电影院门口有个光门,如果有人经过那个门口时就会立即触发光门记录一个时间值,若是离开电影院标记时间属性为out,若是进入电影就标记时间属性为in。求电影院里最大人流量。

    其实把光门记录的时间按从小到大排序,时间值相同时out属性的时间值排在in后面,我们将所有时间值属性按照时间值从小到大遍历一遍,in加1,out减1,中间出现的最大数字即为所求。

     所以本题最重要的是要将线段的端点进行排序。如果排序解决了,按照上面的方法求解就行。

     一般的线段,我们可以按照两端点与(x0,y0)作差形成两个向量(x正半轴到这个向量的到角的大小就是这个向量的“时间值”),利用外积的正负性断定这两个个向量的in属性和out属性。为了讨论方便,我们先把原点平移到(x0,y0)。

     一类比较特殊的线段就是经过原点的线段(不管沿哪个方向引射线都会相交);另一类就是这个时间值发生冲突的线段,如某个线段in点在第四象限,out点在在第一象限(这类似的点),这种情况下,我们就需要将这跟线段拆分成两段!这就保证每个向量能唯一指示一个角度值(“时间值”)。0和360度是重合的,我们可以强制(infinity,0)向量指示360度(infinity为一个大于10000的数)。

  1 /*
  2   Name: poj4048  整点计算极角 
  3   Copyright: 
  4   Author:   Karlvin (指导老师:朱国进 dhu) 
  5   Date: 23/05/12 16:43
  6   Description: 
  7 */
  8 #include <iostream>
  9 #include <set>
 10 #include <cstdio>
 11 #include <algorithm>
 12 using namespace std;
 13 const int size = 10010;
 14 const int inf = 2000010000;
 15 struct point{
 16     int x, y;
 17     int no;  //调试时用的变量,无实际意义 
 18     bool tail;
 19     point(int a = 0, int b = 0,int cn=0,bool t=false){x = a; no = cn; y = b; tail = t;}
 20     point operator =(point t) { x=t.x; y=t.y; no=t.no; tail=t.tail; return *this; }
 21     void print(){ printf("( %d, %d) %d, %d\t",x,y,no,tail); }
 22     friend bool operator < (point p1, point p2){ //按照“到角”大小排序,角度相等时尾节点的角度排在后面 
 23         if(p1.y == 0 && p1.x > 0)
 24         {       if(p1.x >= inf)
 25                      return false;
 26                 if(p2.y==0 && p2.x < inf && p2.x > 0)
 27                 {          if(!(p1.tail^p2.tail)) return false;
 28                            return !p1.tail;   }
 29                 return true;   }
 30         else if(p2.y == 0 && p2.x > 0)
 31         {       if(p2.x >= inf)
 32                      return true;
 33                 return false;   }
 34         if(p1.y * p2.y < 0)
 35             return p1.y > p2.y;
 36         int t = p1.x * p2.y - p1.y * p2.x;
 37         if(t != 0)  return t > 0;
 38         if(!(p1.tail^p2.tail)) 
 39                 return false;
 40         return !p1.tail;
 41     }
 42 };
 43 point start[size];
 44 point end[size];
 45 int xx1[size];
 46 int xx2[size];
 47 int yy1[size];
 48 int yy2[size];
 49 int pxp(int &x1,int &y1,int &x2,int &y2)  //若X积小于0 
 50 {
 51     int ans = x1*y2-x2*y1;
 52     if(ans < 0)
 53     {      swap(x1,x2);
 54            swap(y1,y2); }
 55     return ans;
 56 }
 57 int base,index;
 58 bool cross_x(int x1,int y1,int x2,int y2)  //穿越x正半轴将线段拆分 
 59 {
 60      if(y2 >= 0 && y1 < 0) // 等价于 (x1,y1)X(0,1)  > 0 && (0,1)X(x2,y2) >=0
 61      {        start[index] = point(1,0,index,false);
 62               end[index] = point(x2,y2,index,true);
 63               index++;
 64               start[index] = point(x1,y1,index,false);
 65               end[index] = point(inf,0,index,true);
 66               index++;
 67               return true; }
 68      return false;
 69 }
 70               
 71 int main(){
 72     int cases=0;
 73     cin>>cases;
 74     while(cases--)
 75     {
 76         int n;
 77         cin >> n;
 78         base = 0, index = 0;
 79         int x1,y1,x2,y2,ans;
 80         bool flag = true;
 81         for(int i=0;i<n;i++)
 82                 scanf("%d%d%d%d",&xx1[i],&yy1[i],&xx2[i],&yy2[i]);
 83         int tx,ty;
 84         cin>>tx>>ty;
 85         for (int i = 0; i < n; i++){
 86             x1 = xx1[i]-tx;  x2 = xx2[i]-tx;
 87             y1 = yy1[i]-ty;  y2 = yy2[i]-ty;
 88             ans = pxp(x1,y1,x2,y2);
 89             flag = false;
 90             if(0 == ans && (y1*y2<= 0 || x1*x2 <= 0) ){ //经过原点: 叉积为0,且两点不在坐标轴的同一侧 
 91                 base++;
 92             }else{
 93                   if(ans != 0 && cross_x(x1,y1,x2,y2))
 94                          flag = true;
 95                   if(!flag)
 96                   {     start[index] = point(x1,y1,index,false);
 97                         end[index] = point(x2,y2,index,true);
 98                         index++;    }
 99             }
100         }
101         /*                          //辅助输出 
102         cout<<"index = "<<index<<endl;
103         cout<<"base = "<<base<<endl;
104         for(int i = 0; i < index; i++)
105         {       start[i].print();
106                 end[i].print();
107                 cout<<endl;  }
108         */
109         multiset<point> group;
110         for(int i=0; i<index; i++)
111         {       group.insert(start[i]);
112                 group.insert(end[i]); }
113         multiset<point>::iterator it = group.begin();
114         ans = 0;
115         int max = 0;
116         point t;
117         while(it != group.end())
118         {    //    t = *it;  t.print(); cout<<endl;  //辅助输出 
119                  if(it->tail)
120                      max--;
121                  else  max++;
122                  if(max > ans)
123                         ans = max;
124                  it++; }
125         printf("%d\n",ans+base);
126     }
127     system("pause");
128     return 0;
129 }
View Code

 

转载于:https://www.cnblogs.com/karlvin/archive/2012/05/23/poj4048.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值