[JSOJ2010]部落划分

[JSOJ2010]部落划分

题目链接

题目描述

聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落之间则经常发生争斗。只是,这一切都成为谜团了——聪聪根本就不知道部落究竟是如何分布的。

不过好消息是,聪聪得到了一份荒岛的地图。地图上标注了 nn 个野人居住的地点(可以看作是平面上的坐标)。我们知道,同一个部落的野人总是生活在附近。我们把两个部落的距离,定义为部落中距离最近的那两个居住点的距离。聪聪还获得了一个有意义的信息——这些野人总共被分为了 kk 个部落!这真是个好消息。聪聪希望从这些信息里挖掘出所有部落的详细信息。他正在尝试这样一种算法:

对于任意一种部落划分的方法,都能够求出两个部落之间的距离,聪聪希望求出一种部落划分的方法,使靠得最近的两个部落尽可能远离。

例如,下面的左图表示了一个好的划分,而右图则不是。请你编程帮助聪聪解决这个难题。

插入图片

输入格式

输入文件第一行包含两个整数 nn 和 kk,分别代表了野人居住点的数量和部落的数量。

接下来 nn 行,每行包含两个整数 xx,yy,描述了一个居住点的坐标。

输出格式

输出一行一个实数,为最优划分时,最近的两个部落的距离,精确到小数点后两位。

输入输出样例

输入 #1

4 2
0 0
0 1
1 1
1 0

输出 #1

1.00


输入 #2

9 3
2 2
2 3
3 2
3 3
3 5
3 6
4 6
6 2
6 3

输出 #2

2.00

说明/提示

数据规模与约定

对于100%的数据,保证2 ≤ \leq k ≤ \leq n ≤ \leq 1 0 3 10^3 103,0 ≤ \leq x,y ≤ \leq 1 0 4 10^4 104

解题思路

为了使最近的两个部落最远(目的是尽量远),所以我们可以不断的把不在一个部落且当前距离最短的两个居住点。发现可以用并查集来维护,最后有n-k+1条边,又发现这其实是一道最小生成树的题。

按照边权从小到大的排列,刚开始n个居住点看作是n个部落,每次选取距离最小的两个抱团,部落数–,直到部落数=期待值,此时下一个部落的距离就是最短距离。

数据规模为1000,所以可以放心大胆的Code;

Code

#include<bits/stdc++.h>

using namespace std;
const int maxn=1010;

int F[maxn],siz[maxn],cnt;
double x[maxn],y[maxn];

struct Edge{
    double x,y;
    double weight;
}Ed[maxn*maxn];
bool cmp(Edge a,Edge b){return a.weight<b.weight;}

double dis(int a,int b){
    return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}

void Init_Set(){
    for(int i=0;i<maxn;i++){
        siz[i]=0; F[i]=i;
    }
}

int Find(int x){
    if(F[x]==x) return x;
    return F[x]=Find(F[x]);
}

void Union_Set(int x,int y){
    if((x=Find(x))==(y=Find(y))) return;
    if(siz[x]<siz[y]) F[x]=y;
    else if(siz[x]>siz[y]) F[y]=x;
    else{
        F[x]=y; siz[y]++;
    }
}

int main(){
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>x[i]>>y[i];
    }
    cnt=0;
    for(int i=1;i<n;i++){
        for(int j=i+1;j<=n;j++){
            Ed[++cnt].weight=dis(i,j);
            Ed[cnt].x=i;
            Ed[cnt].y=j;
        }
    }

    sort(Ed+1,Ed+cnt+1,cmp);
    int t=n;
    double ans=0;
    Init_Set();
    for(int i=1;i<=cnt;i++){
        if(t<=k){
            for(int j=i;j<=cnt;j++){
                if(Find(Ed[j].x)!=Find(Ed[j].y)){
                    ans=Ed[j].weight;
                    break;
                }
            }
            break;
        }
        if(Find(Ed[i].x)!=Find(Ed[i].y)){
            Union_Set(Ed[i].x,Ed[i].y);
            t--;
        }
    }
    printf("%.2f\n",ans);
    return 0;
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页