POJ3241 Object Clustering(最小生成树)题解

题意:求最小生成树第K大的边权值

思路:

如果暴力加边再用Kruskal,边太多会超时。这里用一个算法来减少有效边的加入。

边权值为点间曼哈顿距离,那么每个点的有效加边选择应该是和他最近的4个象限方向的点。这里用一个树状数组维护以y-x为索引的y+x的值,然后这个数组所储存的就是一个点的第一象限方向的距离他最近的点。这样我们每次查找只要看(i,N)这个区间是否有一个点的距离比现在的更小(因为以y-x为索引,所以I>i就表示I这个点在i的第一象限方向)。最后每个方向的边都加完后,只要用Kruskal算法加边,加到第K就输出权值。

证明

详解


代码:

#include<queue>
#include<cstring>
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
const int N = 10000+5;
const int INIT = 1061109567;
using namespace std;
int n,k,cnt,Id[N],v[N],f[N];	//id为v值的id序号,v为储存以y-x为索引的y+x值 
struct node{
	int x,y,id;
	friend bool operator < (node a,node b){
		return a.x == b.x? a.y < b.y : a.x < b.x;
	}
}p[N];
struct edge{
	int u,v,w;
	friend bool operator < (edge a,edge b) {
        return a.w < b.w;
    }
}e[N<<2];
int lowbit(int x){
	return x&(-x);
}
void query(int id,int pos,int val){
	pos += 1000;
	int u = -1,ret = INT_MAX;
	for(int i = pos;i < N;i += lowbit(i)){
		if(v[i] < ret && v[i] != INIT){	//找曼哈顿距离最短的
			ret = v[i];
			u = Id[i];
		}
	}
	if(u != -1){	//找到这种点就加一个边
		e[cnt].u = id;
		e[cnt].v = u;
		e[cnt++].w = ret - val;
	}
}
void update(int id,int pos,int val){	//id,y-x,y+x 
	pos += 1000; 
	for(int i = pos;i > 0;i -= lowbit(i)){
		if(val < v[i]){	//更换最佳选择
			v[i] = val;
			Id[i] = id;
		}
	}
}
int find(int x){
	if(x == f[x]) return x;
	else return f[x] = find(f[x]);
}
void kruskal(){
	sort(e,e+cnt);
	for(int i = 0;i < N;i++) f[i] = i;
	int num = n;
	for(int i = 0;i < cnt;i++){
		int x = find(e[i].u);
		int y = find(e[i].v);
		if(x != y){
			f[x] = f[y];
			num--;
			if(num == k){
				printf("%d\n",e[i].w);
				return;
			}
		}
	}
}
void addEdge(){
	memset(v,63,sizeof(v));	//v == 1061109567
	sort(p,p+n);
	for(int i = n-1;i >= 0;i--){
		int index = p[i].y - p[i].x;
		int val = p[i].y + p[i].x;
		query(p[i].id,index,val);
		update(p[i].id,index,val);
	}
}
int main(){
	cnt = 0;
	scanf("%d%d",&n,&k);
	for(int i = 0;i < n ;i++){
		scanf("%d%d",&p[i].x,&p[i].y);
		p[i].id = i;	
	}
	
	addEdge();
	
	for(int i = 0;i < n;i++) swap(p[i].x,p[i].y);
	addEdge();
	
	for(int i = 0;i < n;i++) p[i].y = -p[i].y;
	addEdge();
	
	for(int i = 0;i < n;i++) swap(p[i].x,p[i].y);
	addEdge();
	
	kruskal();
    return 0;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值