可变的圆 二分?排序?

问题源自ACM-ICPC 北京赛区2015网络赛题目1 : The Cats' Feeding Spots
大意是这样的,给出m个点,选其中一个点作为圆心画一个圆能把n个点包含在里面(边界不能有点),求最小的半径,找不到这样的半径输出-1。
自己最开始的思路是这样的,以其中一个点作为圆心,然后用伪二分法查找半径(初始化 low=1,high=1416。1000*2^0.5=1414.213),因为毕竟不是高效正统的二分,所以暂时叫它伪二分吧。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=105;
int n,m;
struct node{
    double x,y;
}p[maxn];
int r[maxn];
double dis(node a,node b){
    return fabs(sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
int in(int dex,int d){
    int res=0;
    double dd=d*1.0;
    for(int i=0;i<m;i++){
         if(dd-dis(p[dex],p[i])>1e-6)
                res++;
    }
    return res;
}
int mf(int dex){
    int low=1,high=1416,mid;  //1000*2^0.5=1415
    while(low<=high){
        mid=(low+high)/2;
        int sum=in(dex,mid);
        if(sum>=n) high=mid-1;
        else return mid;
    }
    return mid;
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int t;
    cin>>t;
    while(t--){
        scanf("%d%d",&m,&n);
        memset(r,-1,sizeof(r));
        for(int i=0;i<m;i++){
            scanf("%lf%lf",&p[i].x,&p[i].y);
        }
        for(int i=0;i<m;i++){
            int w=mf(i);
            if(in(i,w)==n) r[i]=w;
            while(r[i]==-1&&w<=1415){
                w++;
                if(in(i,w)==n) break;
            }
            if(r[i]==-1)r[i]=w;
            for(int j=0;j<m;j++){
                if(fabs(dis(p[i],p[j])-r[i])<1e-6) {
                    r[i]=-1;
                    break;
                }
            }
        }
        int ans=2000;
        for(int i=0;i<m;i++){
            if(r[i]<=1415&&r[i]>=1)ans=min(ans,r[i]);
        }
        if(ans>1415)puts("-1");
        else printf("%d\n",ans);
    }
    return 0;
}

是的,感觉有点乱,而且时间用了800ms。我看了一份更加优秀的代码,它的思路大致是这样,找圆心的部分也是普通的循环遍历,查找最小半径:对于每一个圆心都计算出它和别的点的距离,然后使用快速排序:
for(int j=0;j<m;j++){
    dis[j]=sqrt((data[j].x-data[i].x)*(data[j].x-data[i].x)+(data[j].y-data[i].y)*(data[j].y-data[i].y));
}
sort(dis,dis+m);
包含n个点的半径应该就是(int)dis[n-1]+1,用ans保存最小的值最后输出结果(不贴代码了,没有别人的许可)。我的做法有许多的修正工作,所以计算次数是100*max(log2(100),100)*100,后者的计算是100*100*(log2(100),优劣很快就能区分开。总之,提升水平。。。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值