题目大意:
给出一些点坐标,问有一个半径为1的圆,一次最多能圈多小个点。
思路分析:
(1):一开始想用随机算法(模拟退火)把它水过去得。可是。。。。“火候”一直把握不好,不是WA就是TLE。。。可能这题数据卡的紧,不能用这个方法吧。。。。
(2):第二种思路就是---枚举。应该很容易想的到,枚举任意两个点,把圆心确定下来(本题关键!!),然后遍历所有点,统计在该圆内的点的个数。其中最大值就是答案。。。。。
感想:
(1):模拟退火,火候不好掌握(一般不要首选);
(2):实现枚举时发现函数的形参对代码运行速度也有很大影响。形参为point(1250ms)<--->int(656ms)!!!具体见代码。。。
(3):好像用结构体会比较慢,用二维数组会快一点。。。我没有自己实现。。只是对比了下别人的AC代码。。。
CODE:形参为point(1250ms)
/*几何题*/
/*枚举任意两个点,确定圆心,然后遍历所有点统计在该圆内的点的个数*/
/*AC代码:1250ms*/
#include <iostream>
#include <cmath>
const double eps=1e-6;
const int MAXN=305;
struct point
{
double x,y;
};
struct point ps[MAXN];
int N;
double get_dis(point a,point b)
{
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
void get_center(point a,point b)
{
double ox,oy,dx,dy,r,temp;
ox=(a.x+b.x)/2;
oy=(a.y+b.y)/2;
dx=b.x-a.x;
dy=b.y-a.y;
ps[N].x=ox;ps[N].y=oy;
temp=get_dis(ps[N],b);
r=sqrt(1.0-temp);
if(fabs(dx)<eps)//竖直的情况
ps[N].x-=r;//+或-都可以
else
{
double ang=atan(-dy/dx);
ps[N].x-=r*sin(ang);//+或-都可以
ps[N].y-=r*cos(ang);
}
}
int main()
{
int T,i,j,k,max;
scanf("%d",&T);
while(T--)
{
scanf("%d",&N);
for(i=0;i<N;i++)
scanf("%lf%lf",&ps[i].x,&ps[i].y);
int ans=1;
for(i=0;i<N;i++)//枚举O(n^2)
{
for(j=i+1;j<N;j++)
{
if(get_dis(ps[i],ps[j])>=4) continue;
get_center(ps[i],ps[j]);
for(k=0,max=0;k<N;k++)
{
if(N-k+max<=ans) break;
double temp=sqrt(get_dis(ps[k],ps[N]));
if(temp<=1.000001)
max++;
}
if(max>ans)
ans=max;
}
}
printf("%d\n",ans);
}
return 0;
}
CODE(2):形参为point(656ms)
把两个函数的形参改成int型后即(get_dis(int a,int b)和get_center(int a,int b))时间由1250ms->656ms!!!!
/*几何题*/
/*枚举任意两个点,确定圆心,然后遍历所有点统计在该圆内的点的个数*/
/*AC代码:656ms*/
#include <iostream>
#include <cmath>
const double eps=1e-6;
const int MAXN=305;
struct point
{
double x,y;
};
struct point ps[MAXN];
int N;
double get_dis(int a,int b)
{
return (ps[a].x-ps[b].x)*(ps[a].x-ps[b].x)+(ps[a].y-ps[b].y)*(ps[a].y-ps[b].y);
}
void get_center(int a,int b)
{
double ox,oy,dx,dy,r,temp;
ox=(ps[a].x+ps[b].x)/2;
oy=(ps[a].y+ps[b].y)/2;
dx=ps[b].x-ps[a].x;
dy=ps[b].y-ps[a].y;
ps[N].x=ox;ps[N].y=oy;
temp=get_dis(N,b);
r=sqrt(1.0-temp);
if(fabs(dx)<eps)//竖直的情况
ps[N].x-=r;//+或-都可以
else
{
double ang=atan(-dy/dx);
ps[N].x-=r*sin(ang);//+或-都可以
ps[N].y-=r*cos(ang);
}
}
int main()
{
int T,i,j,k,max;
scanf("%d",&T);
while(T--)
{
scanf("%d",&N);
for(i=0;i<N;i++)
scanf("%lf%lf",&ps[i].x,&ps[i].y);
int ans=1;
for(i=0;i<N;i++)//枚举O(n^2)
{
for(j=i+1;j<N;j++)
{
if(get_dis(i,j)>=4) continue;
get_center(i,j);
for(k=0,max=0;k<N;k++)
{
if(N-k+max<=ans) break;
double temp=sqrt(get_dis(k,N));
if(temp<=1.000001)
max++;
}
if(max>ans)
ans=max;
}
}
printf("%d\n",ans);
}
return 0;
}
CODE(3):模拟退火(WA)、火候张望不好、
/*模拟退火*/
/*WA*/
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>
const int RanN=80;
const int RunN=30;
const double dp=0.7;
const double eps=1e-6;
struct px
{
double x,y;
int num;
};
struct px stu[300+20];
struct px S[RanN+10];
int N,mans;
int get_num(int t)
{
int ans=0;
double temp;
for(int i=1;i<=N;i++)
{
if(N-i+1+ans<=mans)
return 0;
temp=sqrt((double)((S[t].x-stu[i].x)*(S[t].x-stu[i].x)+(S[t].y-stu[i].y)*(S[t].y-stu[i].y)));
if(temp<=1.000001)
//if(temp<1.0001)
ans++;
}
return ans;
}
void get_ans()
{
int i,r,max;
double delta,k1,k2,tx,ty;
mans=1;
delta=(double)10.0/(sqrt(1.0*N));
for(r=1;r<=RanN;r++)
{
S[r].x=double(rand()*1.0/32767)*10.0;
S[r].y=double(rand()*1.0/32767)*10.0;
S[r].num=get_num(r);
}
while(delta>eps)
{
for(r=1;r<=RanN;r++)
{
for(i=1;i<=RunN;i++)
{
k1=double(rand()*1.0/32767)*delta;
k2=sqrt(delta*delta-k1*k1);
if (rand()%2==1) k1*=-1;//定方向
if (rand()%2==1) k2*=-1;
tx=S[r].x;ty=S[r].y;
S[r].x+=k1;
S[r].y+=k2;
if(S[r].x>=0&&S[r].x<=10.0&&S[r].y>=0&&S[r].y<=10.0)
{
max=get_num(r);
if(max>S[r].num)
{
S[r].num=max;
if(max>mans)
mans=max;
}
else
{S[r].x=tx;S[r].y=ty;}
}
}
}
delta*=dp;
}
printf("%d\n",mans);
}
int main()
{
int T,i;
scanf("%d",&T);
while(T--)
{
scanf("%d",&N);
for(i=1;i<=N;i++)
scanf("%lf%lf",&stu[i].x,&stu[i].y);
get_ans();
}
return 0;
}