[ZJOI2010]部落划分

做题日期:2020.11.21

\(【题目描述】\)

在二维平面的第一象限上有\(N(N\leq 10^3)\)个野人居住点,数个野人居住点可以组成一个部落,两个部落之间的距离为这两个部落欧几里德距离最近的两个居住点的距离,已知现在有\(K(K\leq 10^3)\)个部落,问部落之间最近的距离最远是多少(保留两位小数)?

\(【输入样例】\)

4 2
0 0
0 1
1 1
1 0

\(【输出样例】\)

1.00

\(【考点】\)

最小生成树、并查集

\(【做法】\)

将居住点看成点,求出它们两两之间的距离,将它们之间的距离看成边,然后从小到大排序,用并查集维护,每次判断一条边的两端的点是否已经组成一个部落(即是否已经联通),如果不联通则将它们放入一个部落,部落总数减少一,直到部落数等于\(k\)为止,输出此时边权最小且两端居住点不在同一个部落内的边。

\(【代码】\)

#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
const int N=1e3+50;
struct point{
    int x,y;
    double dis;
}a[N*N];
int u[N],v[N];
int fa[N];
int n,k,tot;
 
int Sq(int x){return x*x;}
int Abs(int x){return x<0?-x:x;}
 
double Dist(int sx,int sy,int ex,int ey)//计算两点距离
{
    return (double)sqrt(Sq(Abs(sx-ex))+Sq(Abs(sy-ey)));
}
bool cmp(point a,point b){return a.dis<b.dis;}
int Find(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=Find(fa[x]);
}
 
bool Merge(int x,int y)//判断两个居住点是否在同一个部落
{
    int X=Find(x);
    int Y=Find(y);
    if(X==Y) return true;
    fa[X]=Y;
    return false;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d%d",&u[i],&v[i]);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j) continue;
            tot++;
            a[tot].x=i,a[tot].y=j;
            a[tot].dis=Dist(u[i],v[i],u[j],v[j]);
        }
    }
    for(int i=1;i<=n;i++) fa[i]=i;
    sort(a+1,a+1+tot,cmp);
    int ans=n;
    for(int i=1;i<=tot;i++){
        if(!Merge(a[i].x,a[i].y)) ans--;
        if(ans==k){//当前已经合并成k个部落
            for(int j=i+1;j<=tot;j++){//查找边权最小且两端居住点不在同一个部落的边
                if(!Merge(a[j].x,a[j].y)){
                    printf("%.2f\n",a[j].dis);
                    return 0;
                }
            }
            return 0;
        }
    }
    return 0;
}
posted @ 2020-11-21 17:44  lxzy  阅读(105)  评论(0)    收藏  举报