题目大意:在二维平面中给出 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(固定大小的圆可以覆盖最多的点)相关推荐

  1. 中石油训练赛 - Russian Dolls on the Christmas Tree(树上启发式合并/主席树)

    题目链接:点击查看 题目大意:给出一棵 n 个节点的树,以点 1 为根,现在对于每个节点作为根的子树求解:子树中有多少个编号不相交的连续子段,如:1 2 4 5 7,共有三个连续的段,分别为 [ 1 ...

  2. 中石油训练赛 - Trading Cards(最大权闭合子图)

    题目大意:给出 n 个卡片,可以自由买卖,且价格都是相同的,再给出 m 个集合,如果已经得到了其中一个集合中的卡片,那么可以获得该集合的收益,问如何操作可以使得收益最大化 题目分析:最大权闭合子图的模 ...

  3. 中石油训练赛 - Insertion Order(二叉搜索树+构造)

    题目大意:构造出一个长度为 n 的排列,使得按照这个顺序构造出的二叉搜索树的高度为 k 题目分析:知道 n 的大小后不难算出其可以构造的二叉搜索树高度的可行范围,下限是一棵满二叉树,这个利用倍增很快就 ...

  4. 中石油训练赛 - Watch Later(状压dp)

    题目链接:点击查看 题目大意: 给出一个长度为 n 的字符串,字符串中共有 k 种不同的字符,现在问删除掉所有字符的最小操作数,对于每种字符需要确定一个先后顺序,每次需要删除掉当前所有的这种字符才能去 ...

  5. 中石油训练赛 - Swapity Swap(矩阵快速幂)

    题目描述 Farmer John's N cows (1≤N≤100) are standing in a line. The ith cow from the left has label i, f ...

  6. 中石油训练赛 - Block(二维前缀和+思维)

    题目描述 Alice得到了一张由n×m个黑白像素点组成的图片,她想要压缩这张图片.压缩图片的过程如下: 1.首先,选择一个正整数k(k>1),将图片划分成若干个k×k的小块.如果n,m不能被k整 ...

  7. 中石油训练赛 - Swapping Places(字典序最小的拓扑排序)

    题目链接:点击查看 题目大意:给出 s 个字符串表示种类,再给出 m 个朋友关系,表示两个种类的动物是朋友,现在给出一个长度为 n 的种类排列,规定相邻两个是朋友的种类的动物可以交换位置,问如何操作, ...

  8. 中石油训练赛 - Check List(线段树维护偏序问题)

    题目大意:给出 n 个点,需要计算出满足下列条件的三元对 ( i , j , k ) 的数量: x[ i ] < x[ j ] < x[ k ] y[ k ] > y[ i ] &g ...

  9. 中石油训练赛 - Bad Treap(数学)

    题目链接:点击查看 题目大意:给出笛卡尔树的定义,现在要求给出 n 个点对 ( x , sin( x ) ),使得笛卡尔树的高度尽可能大 题目分析:如果想让笛卡尔树的高度尽可能大,令其退化为一条链即可 ...

最新文章

  1. css 形状_在CSS形状之外思考
  2. JDBC常见面试题集锦(二)
  3. Net中如何操作IIS
  4. 公开课视频与课件(完全免费)-《大企业云桌面部署实战》
  5. JAVA面试常考系列二
  6. 日志服务数据加工最佳实践: 从其他logstore拉取数据做富化
  7. 计算机关闭窗口可以使用alt,禁止使用Alt+F4关闭窗口完整代码
  8. TaskService API
  9. 可以在python3下面用的pyh
  10. cygwin的离线安装包
  11. linux下安装配置nginx,Linux下安装与配置nginx
  12. vue中获取屏幕高度(封装使用)
  13. php根据手机号码获取归属地,PHP通过API获取手机号码归属地,手机号码
  14. 视频结构化技术应用的必要性
  15. 如何让不给听得ge乖乖听话?python教你如何做...
  16. 《Python基础教程(第3版)》笔记:第8章异常
  17. python函数教程:len()方法
  18. 【bzoj3653】谈笑风生
  19. javascript 写农场迭代
  20. 《信息物理融合系统(CPS)设计、建模与仿真——基于 Ptolemy II 平台》——1.10 小结...

热门文章

  1. 人声处理_10款免费的人声处理工具
  2. 如何确定coordinator
  3. MyBatis 缓存详解-一级缓存验证
  4. 代理模式中的动态代理
  5. 逆向推导https的设计过程
  6. TCP/IP的三层负载均衡
  7. 函数的嵌套调用-函数嵌套调用的执行线路图
  8. SpringBoot 配置错误页
  9. linux执行cd会使用系统调用,深入理解Linux系统调用
  10. 【报错笔记】Navicat连接数据库显示2003错误,无法连接到数据库