//manhattan距离下的最小生成树,主要在于产生有效边,然后对产生的有效边集求最小生成树
//POJ3241
//将N个点奉承K个部分,定义两个点的相似度是他们的曼哈顿距离,找出一个数X使得每个点数大于1的部分任意两个点的相似度都不大于X
//转化成manhattan最小生成树求n-k小边问题,可以想成对于最小生成树里面把大于X的边都切掉形成一部分,这一部分可能只有一个点,也可能得到多个点但是他们的manhattan距离不大于X
//复杂度O(n*logn)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
using namespace std ;
const int maxn = 1e5 + 10 ;
const int INF = 0x3f3f3f3f ;
struct Point{
int x, y , id ;
}p[maxn];
//按照x为第一关键字y为第二关键字排序
bool cmp( Point a , Point b ){
if( a.x != b.x )
return a.x < b.x ;
else
return a.y < b.y ;
}
//树状数组,找y-x大于当前的,但是y+x最小的
struct BIT{
int min_val , pos ;
void ini(){
min_val = INF ;
pos = -1 ;
}
}bit[maxn];
//最多4n条有效边而不是n*n
struct Edge{
int u, v , d ;
}edge[maxn << 2];
bool cmpedge( Edge a , Edge b){
return a.d < b.d ;
}
int tot , n , F[maxn] ;
int find(int x ){
if( F[x] == -1)
return x ;
else return F[x] = find( F[x] ) ;
}
void addedge( int u , int v, int d ){
edge[tot].u = u ;
edge[tot].v = v ;
edge[tot ++ ].d = d ;
}
int lowbit( int x ){
return x&( -x ) ;
}
void update( int i , int val , int pos){
while( i>0 ){
if( val < bit[i].min_val ){
bit[i].min_val = val ;
bit[i].pos = pos ;
}
i -= lowbit( i ) ;
}
}
//[i,m]的最小位置
int ask( int i , int m ){
int min_val = INF , pos = -1 ;
while( i <= m ){
if( bit[i].min_val < min_val ){
min_val = bit[i].min_val ;
pos = bit[i].pos ;
}
i += lowbit( i ) ;
}
return pos ;
}
int dist( Point a, Point b ){
return abs( a.x - b.x ) + abs( a.y - b.y ) ;
}
//获取有效边边集
void MNST( int n , Point p[]){
int a[maxn ] , b[maxn] ;
tot = 0 ;
for( int dir = 0 ;dir < 4 ;dir ++){
//四种坐标变换
if( dir == 1 || dir == 3){
for( int i = 0 ;i<n ;i++)
swap( p[i].x , p[i].y ) ;
}
else if( dir == 2){
for( int i = 0 ; i<n ;i++)
p[i].x = -p[i].x ;
}
sort( p , p+n , cmp ) ;
for( int i = 0 ;i<n; i++)
a[i] = b[i] = p[i].y - p[i].x ;
sort( b , b+n) ;
int m = unique( b , b + n ) - b ;
for( int i = 1 ;i<= m ; i++){
bit[i].ini() ;
}
for( int i = n-1 ; i>= 0 ; i--){
int pos = lower_bound( b , b + m , a[i] ) - b + 1 ;
int ans = ask( pos , m ) ;
if( ans != -1)
addedge( p[i].id , p[ans].id , dist( p[i] , p[ans ] )) ;
update( pos , p[i].x + p[i].y , i) ;
}
}
}
//对有效边边集求最小生成树
int solve( int k ){
MNST( n , p ) ;
memset( F , -1 , sizeof( F )) ;
sort( edge , edge + tot , cmpedge ) ;
for( int i = 0 ;i<tot ;i++){
int u = edge[i].u ;
int v = edge[i].v ;
int t1 = find( u ) , t2 = find( v ) ;
if( t1 != t2 ){
F[t1] = t2 ;
k -- ;
if( k == 0 )
return edge[i].d ;
}
}
}
int main(){
int k ;
while( scanf("%d%d" , &n , & k ) ==2 && n ){
for( int i = 0 ; i< n ;i ++ ){
scanf("%d%d" , &p[i].x , &p[i].y ) ;
p[i].id = i ;
}
printf("%d\n" , solve( n - k )) ;
}
return 0 ;
}
manhattan最小生成树
最新推荐文章于 2022-06-30 21:30:27 发布