N个野人居住的地点(可以看作是平面上的坐标)。我们知道,同一个部落的野人总是生活在附近。我们把两个部落的距离,定义为部落中距离最近的那两个居住点的距离。聪聪还获得了一个有意义的信息——这些野人总共被分为了K个部落!这真是个好消息。聪聪希望从这些信息里挖掘出所有部落的详细信息。他正在尝试这样一种算法:
对于任意一种部落划分的方法,都能够求出两个部落之间的距离,聪聪希望求出一种部落划分的方法,使靠得最近的两个部落尽可能远离。
例如,下面的左图表示了一个好的划分,而右图则不是。请你编程帮助聪聪解决这个难题。
输入输出格式
输入格式:
输入文件第一行包含两个整数N和K(1<=N<=1000,1<K<=N),分别代表了野人居住点的数量和部落的数量。
接下来N行,每行包含两个正整数x,y,描述了一个居住点的坐标(0<=x, y<=10000)。
输出格式:
输出一行,为最优划分时,最近的两个部落的距离,精确到小数点后两位。
输入输出样例
输入样例#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
好久没有写题解了,今天看到了一道好题,就写了写。。。。。。。
可能是因为上午的比赛用到了二分,所以我刚刚看到这一道题就马上想到了二分,先说说二分的思路吧
二分最短距离,把每一对距离小于当前值的村庄合并,用并查集维护一下
为了追求更快,所以先把每一对村庄连一条边,并把边权从小到大排序
后来又想了想,这不就是最小生成树吗
为了使最近的两个部落最远(目的是尽量远)
所以我们可以不断的把不在同一部落并且距离最短的两个村庄合并
随后面秒杀了这一道题(编译错误了2次...)
做完这一道题以后,就总结了一下什么时候应该用二分
比如说这中类型的题目中,答案已经在数据中(这里是两个点的距离),就可以不用二分,因为二分可能存在精度问题
当结果为某个时间之类的不确定的结果,就可以用二分,这些还是要靠平时的做题来积累
参考代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std ;
const int N = 1e3 + 10 ;
int x[N] , y[N] , n , k ;
struct edge {
int u , v ; double w ;
} e[N*N] ; int tot ;
inline bool cmp ( edge n1 , edge n2 ) {
return n1.w < n2.w ;
}
int _( int x ) { return x * x ; }
double get ( int n1 , int n2 ) {
return sqrt ( double ( _( x[n1] - x[n2] ) + _( y[n1] - y[n2] ) ) ) ;
}
int fa[N] ;
int findfa ( int x ) {
if ( x == fa[x] ) return x ;
return fa[x] = findfa ( fa[x] ) ;
}
int main() {
cin >> n >> k ;
for ( int i = 1 ; i <= n ; i ++ )
cin >> x[i] >> y[i] ;
for ( int i = 1 ; i < n ; i ++ )
for ( int j = i + 1 ; j <= n ; j ++ )
e[++tot] = (edge) { i , j , get(i,j) } ;
sort ( e + 1 , e + tot + 1 , cmp ) ; int s = n ;
for ( int i = 1 ; i <= n ; i ++ ) fa[i] = i ;
for ( int i = 1 ; i <= tot ; i ++ ) {
int fx = findfa ( e[i].u ) ;
int fy = findfa ( e[i].v ) ;
if ( fx == fy ) continue ;
if ( s == k ) {
printf ( "%.2lf\n" , e[i].w ) ; break ;
}
s -- ; fa[fx] = fy ;
}
return 0 ;
}