中石油训练赛 - Gone Fishing(固定大小的圆可以覆盖最多的点)
題目大意:在二維平面中給出 n 個點,再給出一個固定大小的圓,問如何放置這個圓可以使其覆蓋最多的點
題目分析:首先不難想到一種 n^3 的做法,就是兩層循環去枚舉兩個點,因為兩個不同的點就可以確定下來兩個圓了(對稱的),然后對于這 2 * n * n 個圓的每一個來說,再用一層循環去枚舉 n 個點,計算一下有多少個點可以被覆蓋到就可以了
考慮優化,假如分別以點 i 和點 j 為圓心,以 r 為半徑做出兩個相交的圓,比較顯然的是,如果在相交的陰影部分中任選一點作為圓心,同樣以 r 作為半徑做圓,那么點 i 和點 j 都可以同時被覆蓋到,如下圖所示:
所以我們不妨映射到其中一個圓的弧上,稱這一段為相交弧,這樣一來此題就得以優化了:
先用一層循環去固定點 i 作為圓心,然后枚舉點 j 同樣也作為圓心,兩個圓若能相交的話求出相交弧,最多有 n 段相交弧,對于以點 i 為圓心的圓周來說,其中某個點被覆蓋的次數,就是以該點為圓心所能覆蓋的點數,所以求出被覆蓋最多的位置即可
如何去求這個位置呢?利用差分的思想,對 n 段相交弧,也就是 2 * n 個交點進行極角排序,然后掃一遍求最大連續子段和就是答案了,時間復雜度為 n^2logn
很讓人煩心的一點是,這個題目用 atan2 的極角排序很輕松 AC,但用叉積排序總是多多少少會出現一些不可描述的問題,一直卡在 97 分的位置,糾結三天了,沒精力再耗下去了。。就這樣隨緣吧
代碼:
n^3
//#pragma GCC optimize(2) //#pragma GCC optimize("Ofast","inline","-ffast-math") //#pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=110; // `計算幾何模板` const double eps = 1e-8; const double pi = acos(-1.0); const int maxp = 1010; //`Compares a double to zero` int sgn(double x){if(fabs(x) < eps)return 0;if(x < 0)return -1;else return 1; } struct Point{double x,y;Point(){}Point(double _x,double _y){x = _x;y = _y;}void input(){scanf("%lf%lf",&x,&y);}void output(){printf("%.2f %.2f\n",x,y);}bool operator == (Point b)const{return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;}bool operator < (Point b)const{return sgn(x-b.x)== 0?sgn(y-b.y)<0:x<b.x;}Point operator -(const Point &b)const{return Point(x-b.x,y-b.y);}//叉積double operator ^(const Point &b)const{return x*b.y - y*b.x;}//點積double operator *(const Point &b)const{return x*b.x + y*b.y;}//返回長度double len(){return hypot(x,y);//庫函數}//返回兩點的距離double distance(Point p){return hypot(x-p.x,y-p.y);}Point operator +(const Point &b)const{return Point(x+b.x,y+b.y);}//`化為長度為r的向量`Point trunc(double r){double l = len();if(!sgn(l))return *this;r /= l;return Point(x*r,y*r);}//`逆時針旋轉90度`Point rotleft(){return Point(-y,x);}//`順時針旋轉90度`Point rotright(){return Point(y,-x);} }point[N]; //圓 struct circle{Point p;//圓心double r;//半徑circle(){}circle(Point _p,double _r){p = _p;r = _r;}circle(double x,double y,double _r){p = Point(x,y);r = _r;}//輸入void input(){p.input();scanf("%lf",&r);}//輸出void output(){printf("%.2lf %.2lf %.2lf\n",p.x,p.y,r);}bool operator == (circle v){return (p==v.p) && sgn(r-v.r)==0;}bool operator < (circle v)const{return ((p<v.p)||((p==v.p)&&sgn(r-v.r)<0));}//`點和圓的關系`//`0 圓外`//`1 圓上`//`2 圓內`int relation(Point b){double dst = b.distance(p);if(sgn(dst-r) < 0)return 2;else if(sgn(dst-r)==0)return 1;return 0;}//`兩圓的關系`//`5 相離`//`4 外切`//`3 相交`//`2 內切`//`1 內含`//`需要Point的distance`//`測試:UVA12304`int relationcircle(circle v){double d = p.distance(v.p);if(sgn(d-r-v.r) > 0)return 5;if(sgn(d-r-v.r) == 0)return 4;double l = fabs(r-v.r);if(sgn(d-r-v.r)<0 && sgn(d-l)>0)return 3;if(sgn(d-l)==0)return 2;if(sgn(d-l)<0)return 1;}//`求兩個圓的交點,返回0表示沒有交點,返回1是一個交點,2是兩個交點`//`需要relationcircle`//`測試:UVA12304`int pointcrosscircle(circle v,Point &p1,Point &p2){int rel = relationcircle(v);if(rel == 1 || rel == 5)return 0;double d = p.distance(v.p);double l = (d*d+r*r-v.r*v.r)/(2*d);double h = sqrt(r*r-l*l);Point tmp = p + (v.p-p).trunc(l);p1 = tmp + ((v.p-p).rotleft().trunc(h));p2 = tmp + ((v.p-p).rotright().trunc(h));if(rel == 2 || rel == 4)return 1;return 2;}//`得到過a,b兩點,半徑為r1的兩個圓`static int gercircle(Point a,Point b,double r1,circle &c1,circle &c2){circle x(a,r1),y(b,r1);int t = x.pointcrosscircle(y,c1.p,c2.p);if(!t)return 0;c1.r = c2.r = r1;return t;} };int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);double r;scanf("%lf",&r);int n;scanf("%d",&n);for(int i=1;i<=n;i++)point[i].input();int ans=1;for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++){circle c1,c2;circle::gercircle(point[i],point[j],r,c1,c2);int sum1=0,sum2=0;for(int k=1;k<=n;k++){if(c1.relation(point[k]))sum1++;if(c2.relation(point[k]))sum2++;}ans=max(ans,sum1);ans=max(ans,sum2);}printf("%d\n",ans);return 0; }n^2logn
//#pragma GCC optimize(2) //#pragma GCC optimize("Ofast","inline","-ffast-math") //#pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=110;const double eps=1e-8;int sgn(double x) {if(fabs(x)<=eps)return 0;if(x<0)return -1;return 1; }struct Point {double x,y;void input(){scanf("%lf%lf",&x,&y);}double distance(const Point& t)const{return hypot(x-t.x,y-t.y);} }point[N];struct Node {double alpha;int flag;Node(double alpha,int flag):alpha(alpha),flag(flag){}bool friend operator<(const Node& a,const Node& b){if(sgn(a.alpha-b.alpha)==0)return a.flag>b.flag;return sgn(a.alpha-b.alpha)<0;} };int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);double r;scanf("%lf",&r);int n;scanf("%d",&n);for(int i=1;i<=n;i++) point[i].input();int ans=1;for(int i=1;i<=n;i++){vector<Node>node;for(int j=1;j<=n;j++){if(i==j)continue;double dis=point[i].distance(point[j]);if(sgn(dis-2*r)>0)continue;double alpha=atan2(point[j].y-point[i].y,point[j].x-point[i].x);double phi=acos(dis/(2.0*r));node.push_back(Node(alpha-phi,1));node.push_back(Node(alpha+phi,-1));}sort(node.begin(),node.end());int sum=0;for(auto it:node){sum+=it.flag;ans=max(ans,sum+1);}}printf("%d\n",ans);return 0; }?
總結
以上是生活随笔為你收集整理的中石油训练赛 - Gone Fishing(固定大小的圆可以覆盖最多的点)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中石油训练赛 - Russian Dol
- 下一篇: 中石油训练赛 - Spiral Matr