高精度的极角计算是种很实用的算法。由于计算机采用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 }