洛谷P4047 [JSOI2010]部落划分 题目 思路 代码

https://www.luogu.com.cn/problem/P4047

思路

关键句:“使靠得最近的两个部落尽可能远离”,二分答案经典套路。

那么我们关心的就是check函数怎么写了,事实上,由于(n)很小,我们完全可以(n^2)枚举每两个点是否能分在不同部落中,如果不能就在两个点间连一条边,

再写个并查集维护下连通块,细节见代码,时间复杂度(n^2logx)(x是坐标的大致范围),注意精度问题。

代码

#include<cstdlib>
#include<algorithm>
#include<cmath>
#define eps 1e-4
#define maxn 1001
#define db double
using namespace std;
int n,k;
struct point{
    int x,y;
    bool operator <(point t){
        return x<t.x;
    }
} p[maxn];
db dis(int i,int j){
    db x2,y2;
    x2=p[i].x-p[j].x;
    y2=p[i].y-p[j].y;
    return sqrt(x2*x2+y2*y2);
}
int fa[maxn];
int getf(int x){
    if(fa[x]==x) return x;
    fa[x]=getf(fa[x]);
    return fa[x];
}
int merge(int a,int b){
    int f1=getf(a),f2=getf(b);
    if(f1==f2) return 0;
    else{
        fa[f2]=f1;
        return 1;
    }
}
bool check(db limit){
    int cnt=n,i,j;
    for(i=1;i<=n;++i)
        fa[i]=i;
    for(i=1;i<=n;++i){
        for(j=i+1;j<=n;++j){
            if(p[j].x-p[i].x>limit) break;
            if(dis(i,j)<=limit){
                cnt-=merge(i,j);
            }
        }
    }
    return cnt>=k;
}
int main(){
    int i,j;
    db l=0,r=14200;
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;++i)
        scanf("%d%d",&p[i].x,&p[i].y);
    sort(p+1,p+n+1);
    while(r-l>eps){
        db mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    printf("%.2lf",l);
    // system("pause");
    return 0;
}