洛谷P4047 [JSOI2010]部落划分

该博客探讨了一种优化部落划分的问题,目标是使最近的两个部落间的距离最大化。作者通过二分搜索和最小生成树算法解决了这个问题,并提供了输入输出格式、样例以及解题思路和参考代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值