首页 > 代码库 > POJ 2451-半平面交
POJ 2451-半平面交
半平面交第一次真的好难写啊。。前后加起来起码弄了7小时。
半平面交的算法思想并不复杂:
1、将所有向量按极角大小从小到大排序,向量(x,y)的极角=atan2(y,x),当两个向量的极角相等时选择性的保留在较左边的那个
2、运用一种数据结构:双端队列,用phead表示双端队列的头位置,pend表示双端队列的尾位置,初始化时phead=0,pend=-1
然后开始一个一个添加已经排好序的向量进双端队列中:
设当前要添加的向量是v
#当双端队列中的向量数量大于等于2时(pend-phead>=1) 并且 双端队列最尾部的两个向量的交点在向量v所表示的半平面外,删除位于双端队列最尾部的那个向量(pend--)
#当双端队列中的向量数量大于等于2时 并且 双端队列最头部的两个向量的交点在向量v所表示的半平面外,删除位于双端队列最头部的那个向量(phead++)
进行外上面两个步骤后,添加当前向量v到双端队列的尾部
3、当排好序的向量全部添加完之后,还需要一次判定,过程和上一步类似:
#当双端队列中的向量数量大于2时,如果双端队列尾部的两个向量的交点在 最头部的向量所表示的半平面外,删除位于双端队列最尾部的那个向量
#当双端队列中的向量数量大于2时,如果双端队列头部的两个向量的交点在 最尾部的向量所表示的半平面外,删除位于双端队列最头部的那个向量
4、依次求双端队列相邻两个向量的交点 即为 所有半平面的交围成的凸多边形的顶点(当然也有可能是非封闭图形或者线段、点,甚至是空集)
有一个非常重要的地方一定要注意:
每次一定要先判断双端队列最尾部的向量该不该删,再判断双端队列头部的向量该不该删。因为双端队列中的向量的极角是从小到大有序的,当新插入一个向量v时,应该先考虑与向量v的极角最相近的向量该不该删。
贴一个自己的半平面交的模板:(POJ2451标程)
1 #include<iostream> 2 #include<cmath> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<algorithm> 6 #define maxn 20005 7 #define EPS 1e-10 8 using namespace std; 9 int n,sum; 10 int phead=0,pend=-1; 11 struct Point{double x,y;}; 12 13 struct VecLine{ 14 double x0,y0,vx,vy; //(x0,y0)表示该向量上的一点,(vx,vy)表示该向量的方向 15 double angle; 16 friend bool operator<(const VecLine a,const VecLine b){ 17 if(fabs(a.angle-b.angle)<=EPS){ 18 double x1=a.vx,y1=a.vy; 19 double x2=b.x0-a.x0,y2=b.y0-a.y0; 20 return (x1*y2-x2*y1<0); 21 } 22 return a.angle<b.angle; 23 } 24 }line[maxn],deque[maxn]; 25 26 void AddVecLine(double x0,double y0,double vx,double vy){ 27 line[sum++]=((VecLine){x0,y0,vx,vy,atan2(vy,vx)}); 28 } 29 void init(){ 30 scanf("%d",&n); 31 double x1,x2,y1,y2; 32 for(int i=0;i<n;i++){ 33 scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2); 34 AddVecLine(x1,y1,x2-x1,y2-y1); 35 } 36 AddVecLine(0,0,10000,0); 37 AddVecLine(10000,0,0,10000); 38 AddVecLine(10000,10000,-10000,0); 39 AddVecLine(0,10000,0,-10000); 40 41 sort(line,line+sum); 42 int tot=0; 43 for(int i=1;i<sum;i++){ 44 if(fabs(line[i].angle-line[tot].angle)>EPS) line[++tot]=line[i]; 45 46 } 47 sum=++tot; 48 49 } 50 Point Get_cross_point(VecLine A,VecLine B){ 51 double x0=A.x0,y0=A.y0,x1=A.vx,y1=A.vy; 52 double x2=B.x0,y2=B.y0,x3=B.vx,y3=B.vy; 53 double k=(y2*x1+y1*x0-x1*y0-y1*x2)/(x3*y1-y3*x1); 54 double x=x2+k*x3,y=y2+k*y3; 55 return ((Point){x,y}); 56 } 57 58 bool Judge(VecLine A,VecLine B,VecLine C){//Judge函数用于判断向量A和向量B的交点是否在向量C的左侧 59 Point p=Get_cross_point(A,B); 60 p.x=p.x-C.x0;p.y=p.y-C.y0; 61 return (p.x*C.vy-p.y*C.vx<0); 62 } 63 64 void Get_Half_Plane(){ 65 for(int i=0;i<sum;i++){ 66 VecLine X=line[i]; 67 while((pend-phead)>=1 && !Judge(deque[pend],deque[pend-1],X)) pend--; 68 while((pend-phead)>=1 && !Judge(deque[phead],deque[phead+1],X)) phead++; 69 pend++;deque[pend]=X; 70 } 71 while((pend-phead)>=2 && !Judge(deque[pend],deque[pend-1],deque[phead])) pend--; 72 while((pend-phead)>=2 && !Judge(deque[phead],deque[phead+1],deque[pend])) phead++; 73 } 74 void GetArea(){ 75 double S=0; 76 Point ans[maxn]; 77 int t=0; 78 for(int i=phead;i<=pend;i++){ 79 int j=i+1;if (j>pend) j=phead; 80 ans[t++]=Get_cross_point(deque[i],deque[j]); 81 82 } 83 ans[t]=ans[0]; 84 for(int i=0;i<t;i++){ 85 S+=(ans[i].x*ans[i+1].y-ans[i+1].x*ans[i].y); 86 } 87 S=fabs(S/2); 88 printf("%.1f\n",S); 89 } 90 91 92 int main(){ 93 init(); //读入数据,并添加到存向量的数组,并按极角排序 94 Get_Half_Plane(); //求出半平面交 95 GetArea(); //计算面积 96 return 0; 97 }
POJ 2451-半平面交