题目
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;
}