题目:
POJ 1981 Circle and Points
HDU 1077 Catching Fish
题意:
给出n个点的二维点坐标,问单位圆最多能覆盖多少点?
分析:
①:
最优的情况一定是有两个点在圆弧上。先枚举两个点,计算两点在圆弧上的单位圆(一般会有两个)
但是可以统一取一个方向的(也就是AB取一个然后BA取另外一个).然后枚举所有点,计算在这个单位圆内的点的个数。
这样做的时间复杂度是O(n^3).
②:
对每个点以R为半径画圆,对N个圆两两求交。这一步O(N^2)。问题转化为求被覆盖次数最多的弧。
因为如果最优圆覆盖A点那么最优圆一定在以A点为圆心的圆弧上。那么圆弧倍覆盖多少次也就意味着
以这条圆弧为上任意一点为圆心花园能覆盖多少点。
对每一个圆,求其上的每段弧重叠次数。假如A圆与B圆相交。A上[PI/3, PI/2]的区间被B覆盖(PI为圆周率)。
那么对于A圆,我们在PI/3处做一个+1标记,在PI/2处做一个-1标记。
对于[PI*5/3, PI*7/3]这样横跨0点的区间只要在0点处拆成两段即可。
将一个圆上的所有标记排序,从头开始扫描。初始total=0,碰到+1标记给total++,碰到-1标记total–。
扫描过程中total的最大值就是圆上被覆盖最多的弧。求所有圆的total的最大值就是答案。
极角排序需要2*n*logn,总复杂度O(N^2 * logN)
//1264K 1357MS
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cmath>
using namespace std;
const int MAX_N=310;
const double eps=1e-6;
const double R=1.0;//定义覆盖圆半径为R
int T,n;
struct Point{
double x,y;
}point[MAX_N];
inline double dis(Point a,Point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
inline Point GetCenter(Point a,Point b)
{//获取a,b两点在圆周上的单元圆圆心,单位圆圆心有两个
struct Point mid,res;
mid.x=(a.x+b.x)/2,mid.y=(a.y+b.y)/2;//mid是a,b中点坐标
double angle=atan2(b.y-a.y,b.x-a.x);//angle是直线ab的倾斜角
double tmp=dis(a,b)/2;//tmp是线段ab长度的一半
double d=sqrt(1.0-tmp*tmp);//d是ab中点到圆心的距离
res.x=mid.x-d*sin(angle);//res是直线ab左边的那个圆心
res.y=mid.y+d*cos(angle);
//下面的res是直线ab右边的那个圆心
//res.x=mid.x+d*sin(angle);
//res.y=mid.y-d*cos(angle);
return res;
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%lf%lf",&point[i].x,&point[i].y);
}
int ans=1;//初始化至少能覆盖一个点!!!
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j||dis(point[i],point[j])-2*R>eps) continue;
int cnt=0;
struct Point center=GetCenter(point[i],point[j]);
for(int k=0;k<n;k++){
if(dis(point[k],center)-R<=eps) cnt++;
}
ans=max(ans,cnt);
}
}
printf("%d\n",ans);
}
return 0;
}
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAX_N=310;
const double PI=acos(-1.0);
const double R=1.0;//定义覆盖圆半径为R
int T,n,total;
struct Point{
double x,y;
}point[MAX_N];
struct Angle{
double data;
int is;
bool operator < (const Angle& rhs) const {
return data<rhs.data;
}
}angle[MAX_N*2];
inline double Dis(Point a,Point b)//计算线段ab的长度
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
inline double God(Point a,Point b)//计算向量ab的极角
{
double res=atan(fabs((b.y-a.y)/(b.x-a.x)));
if(b.y<a.y){
if(b.x<a.x) res+=PI;
else res=2*PI-res;
}else {
if(b.x<a.x) res=PI-res;
}
return res;
}
void solve()
{
int res=1;
for(int i=0;i<n;i++){
total=0;
for(int j=0;j<n;j++){
if(i==j) continue;
double dist=Dis(point[i],point[j]);
if(dist>2*R) continue;
double base=God(point[i],point[j]);
double extra=acos(dist/2.0); //计算差角
angle[total].data=base+extra;
angle[total++].is=-1;
angle[total].data=base-extra;
angle[total++].is=1;
}
if(total<=res) continue;
sort(angle,angle+total);
int tmp=1;
for(int j=0;j<total;j++){
tmp+=angle[j].is;
res=max(res,tmp);
}
}
printf("%d\n",res);
}
int main()
{
//freopen("1077in.txt","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%lf%lf",&point[i].x,&point[i].y);
}
solve();
}
return 0;
}

该博客介绍了如何在给定n个二维点坐标的情况下,找到能够覆盖最多点的单位圆。首先分析了两种方法:一是枚举两个点形成单位圆并计算覆盖点数,二是对每个点构建圆并求圆与圆的交集,通过极角排序计算被覆盖最多的弧。最终,第二种方法的时间复杂度为O(N^2 * logN)。
979

被折叠的 条评论
为什么被折叠?



