zoukankan      html  css  js  c++  java
  • LA2572计算几何+离散化+面的覆盖

      1 /*
    后来看到的极角排序,值得一看:http://blog.csdn.net/zxy_snow/article/details/6700847
    2 LA2572计算几何 3 4 解题思路: 5 1、小圆面是由小圆弧围成。那么找出每条小圆弧,如果小圆弧,在小圆弧中点上下左右进行微小位移的所得的点一定在一个小圆面内。 6 找到最后覆盖这个小点的圆一定是可见的。 7 2、圆上的点按照相邻依次排序的关键量为极角(0,2PI) 8 3、用中心点代替圆弧本身是否被圆覆盖 9 10 11 感悟: 12 这道题是一道计算几何思维非常综合的题目,如下: 13 1、离散化和按顺序扫描: 14 因为几何图形是连续的,而计算机只能处理一些离散的测试点。所以找到关键的离散化方法很重要。 15 就像这道题:关键问题:可见的这些小圆面是属于哪个圆的? 16 离散测试点找到所有小圆面中至少一个点。所以我们离散的过程要保证找到所有的情况,所以枚举了所有的圆弧。 17 类似的问题: 18 LA4127山的轮廓线,离散点是直线与直线的交点 19 UVA10969美梦,离散点是圆的交点 20 UVA11177多边形怪兽,离散点是直线与圆的交点 21 2、添加首尾测试点(离散边界) 22 例如这道的: 23 Arf[0]=0.0; 24 Arf[1]=2*M_PI; 25 3、放大法保留精度 26 例: 27 cin>>x>>y>>r; 28 x=1e10*x;y=1e10*y;r=1e10*r;//这里非常重要,因为我们是进行点的微小移动,这样等于将整张图放大了,保留了精度 29 30 */ 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <math.h> 35 #include <ctype.h> 36 #include <string> 37 #include <iostream> 38 #include <sstream> 39 #include <vector> 40 #include <queue> 41 #include <stack> 42 #include <map> 43 #include <list> 44 #include <set> 45 #include <algorithm> 46 #define rec(i,n) for(int i=1;i<=n;i++) 47 #define INF 0x3f3f3f3f 48 #define eps 1e-7 49 #define eps2 1e-3 50 using namespace std; 51 52 double move[5][2]= {{0,eps2},{0,-eps2},{eps2,0},{-eps2,0}}; 53 //微小偏移量,至少是3个不同的方向,这里取上下左右四个比较方便,记住偏移量的数量就比eps本身要大,不然等于无效 54 struct Point 55 { 56 double x,y; 57 Point() {} 58 Point(double xx,double yy) 59 { 60 x=xx; 61 y=yy; 62 } 63 }; 64 struct Circle 65 { 66 Point O; 67 double r; 68 Circle() {} 69 Circle(Point O1,double r1) 70 { 71 O=O1; 72 r=r1; 73 } 74 Point point(double a) 75 { 76 return Point(O.x+cos(a)*r,O.y+sin(a)*r); 77 } 78 }; 79 typedef Point Vector; 80 81 bool operator==(Point A,Point B) 82 { 83 if ((fabs(A.x-B.x)<1e-10) && (fabs(A.y-B.y)<1e-10)) return true; 84 else return false; 85 } 86 Vector operator-(Point A,Point B)//表示A指向B 87 { 88 return Vector(A.x-B.x,A.y-B.y); 89 } 90 Vector operator*(Vector A,double k) 91 { 92 return Vector(A.x*k,A.y*k); 93 } 94 Vector operator+(Point A,Point B)//表示A指向B 95 { 96 return Vector(B.x+A.x,B.y+A.y); 97 } 98 double Dot(Vector A,Vector B) 99 { 100 return A.x*B.x+A.y*B.y; 101 } 102 double Length(Vector A) 103 { 104 return sqrt(Dot(A,A)); 105 } 106 double Cross(Vector A,Vector B) 107 { 108 return A.x*B.y-A.y*B.x; 109 } 110 int dcmp(double x) 111 { 112 if(fabs(x)<1e-10) return 0; 113 else if(x>0) return 1; 114 else return -1; 115 } 116 double angle(Vector v) 117 { 118 return atan2(v.y,v.x); 119 } 120 int getCircleInter(Circle c1, Circle c2,vector<Point>& sol1,vector<Point>& sol2) 121 { 122 double d=Length(c1.O-c2.O); 123 if(dcmp(d)==0) 124 { 125 if(dcmp(c1.r-c2.r)==0) return -1;//两圆重合 126 return 0; 127 } 128 if(dcmp(c1.r+c2.r-d)<0) return 0; 129 if(dcmp(fabs(c1.r-c2.r)-d)>0) return 0; 130 131 double a=angle(c2.O-c1.O); 132 double da=acos((c1.r*c1.r+d*d-c2.r*c2.r)/(2*c1.r*d)); 133 134 Point p1=c1.point(a-da),p2=c1.point(a+da); 135 136 if (p1==p2) return 1; 137 sol1.push_back(p1),sol2.push_back(p1); 138 sol1.push_back(p2); 139 sol2.push_back(p2); 140 return 2; 141 } 142 int n; 143 Circle CC[110];// 144 vector<Point> CP[110];//每个圆上的交点 145 double Arf[410];//圆心角 146 double getangle(Vector v)//核心函数,返回向量的极角 147 { 148 double x=v.x; 149 double y=v.y; 150 if(fabs(y)<eps) if(x>0) return 0; 151 else return M_PI; 152 if(fabs(x)<eps) if(y>0) return 0.5*M_PI; 153 else return 1.5*M_PI; 154 double arf=acos(fabs(x)/(sqrt(x*x+y*y))); 155 if(x>0) if(y>0) return arf; 156 else return 2*M_PI-arf; 157 if(y>0) return M_PI-arf; 158 else return M_PI+arf; 159 } 160 Point GetMid(double a,double b,double r,double x,double y)//圆上两点,获得中点的坐标 161 { 162 double arf=(b+a)/2; 163 double xx=r*cos(arf)+x; 164 double xy=r*sin(arf)+y; 165 Point P=Point(xx,xy); 166 // P.print(); 167 return P; 168 } 169 bool PInCircle(Point P,Circle C)//判断点在圆内 170 { 171 double dis=Length(P-C.O); 172 if (dis-C.r>1e-10) return false; 173 else return true; 174 } 175 bool see[410];//圆是否可见 176 int main() 177 { 178 while(cin>>n && n>0) 179 { 180 memset(see,0,sizeof(see)); 181 see[n]=true;//最后一个圆一定可见,不必判断 182 for(int i=1; i<=n; i++)//读取信息 183 { 184 double x,y,r; 185 cin>>x>>y>>r; 186 x=1e10*x;y=1e10*y;r=1e10*r;//这里非常重要,因为我们是进行点的微小移动,这样等于将整张图放大了,保留了精度 187 CC[i]=Circle(Point(x,y),r); 188 } 189 for(int i=1; i<=n; i++) CP[i].clear(); 190 for(int i=1; i<n; i++)//获得圆上的交点 191 for(int j=i+1; j<=n; j++) 192 getCircleInter(CC[i],CC[j],CP[i],CP[j]); 193 for(int i=1; i<=n; i++) //枚举前n-1个圆上的圆弧 194 {//当CP[i].size()为0时要特判,这是和其他圆的关系要么包含,要么被包含,比较半径即可 195 int k=CP[i].size(); 196 Arf[0]=0.0; 197 Arf[1]=2*M_PI;//添加两个点,局部细分:这里也是重点:图形离散化解决了穷举的难题,同时也意味着可加上一些特殊的点 198 //上面两个点的加入,也解决了圆上0和1个点的问题 199 for(int j=0; j<k; j++) 200 { 201 double arf=getangle(CP[i][j]-CC[i].O);//重点:角度的计算,其实就是圆心指向圆上的点的向量的角度啊 202 Arf[j+2]=arf; 203 } 204 sort(Arf,Arf+k+2); 205 k=unique(Arf,Arf+k+2)-Arf;//去除重复点 206 207 for(int j=0; j<k; j++) //枚举每条弧 208 { 209 Point Mid=GetMid(Arf[j],Arf[(j+1)%k],CC[i].r,CC[i].O.x,CC[i].O.y); 210 211 bool ok=true;//表示这条圆弧可见 212 for(int l=i+1; l<=n; l++) //判断中点是否可见 213 if (PInCircle(Mid,CC[l])) ok=false; 214 if(ok)//这条弧可见 215 { 216 int num[5]= {-1,-1,-1,-1}; 217 Point M[5]; 218 for(int l=0; l<4; l++)//向四个方向上枚举微小的位移点 219 { 220 M[l].x=Mid.x+move[l][0]; 221 M[l].y=Mid.y+move[l][1]; 222 } 223 for(int ll=0; ll<4; ll++) 224 for(int l=1; l<=n; l++) //判断最终覆盖可见点的圆的序号 225 if(PInCircle(M[ll],CC[l])) num[ll]=l; 226 for(int ll=0; ll<4; ll++) 227 if(num[ll]!=-1) see[num[ll]]=true; 228 } 229 } 230 } 231 232 int ans=0; 233 for(int i=1; i<=n; i++) if(see[i]) ans++; 234 cout<<ans<<endl; 235 } 236 return 0; 237 }
  • 相关阅读:
    js去java的变量
    android:layout_alignLeft=""和android:layout_toLeftOf=""
    c:foreach遍历list
    fragment的getActivity和activity的getapplication实现共享方法和变量
    java.lang.IllegalStateException: commit already called
    选择图片时调用手机拍摄和相册图片
    android:duplicateParentState属性
    java日期事件处理
    汉字字符串编码转换
    JAVA入门到精通-第5讲-类和对象
  • 原文地址:https://www.cnblogs.com/little-w/p/3570209.html
Copyright © 2011-2022 走看看