poj 3241 Object Clustering 曼哈顿最小生成树

题意:

       平面上有n个点,现在把他们分成k个集合,使得每个集合中的每个点都至少有一个本集合的点之间的曼哈顿距离不大于X,求最小的X。

分析:

       转化为求n个点生成完全图的最小生成树的第k大边。接下来有几个重点。

1)根据莫队算法,由于边权是点的曼哈顿距离,每个点只需要跟周围8个方向中每个方向最近的点连边,这样算出的图与用完全图算出的最小生成树一样,涉及的边却大大减少。

2)用树状数组维护y右偏45度的最近点,每个点以y-x的位置,y+x的值放入树状数组,由于每次是查询区间(pos,last)的情况,所以树状数组c[i]的覆盖范围要改成a[i,i+1,...a+2^k-1],k是i二进制末尾0的个数。查询和更新也有相应改动。

3)对最小生成树一个猜想:设一个图的最小生成树的各边按权从小到大排序为a1,a2....an-1,该图任意生成树的各边按权从小到大排序为b1,b2,..bn-1,则ai<=bi(i=1,2,...n-1)。

代码:

//poj 3241
//sep9
#include <iostream>
#include <algorithm>
const int maxN=10024;
using namespace std;
int n,k,e;
struct P
{
	int x,y,ids;
}p[maxN];
struct EDGE
{
	int u,v,w;		
}edge[maxN*4];
int c[4000],d[4000],fa[maxN];

int lowbit(int x)
{
	return x&(x^(x-1));
}

int cmp_p(P a,P b)
{
	if(a.x!=b.x)
		return a.x<b.x;
	return a.y<b.y;	
}

int cmp_e(EDGE a,EDGE b)
{
	return a.w<b.w;
}
void query(int ids,int pos,int val)
{
	pos+=1000;
	int t_ids=-1,ret=INT_MAX;
	for(int i=pos;i<4000;i+=lowbit(i))
		if(ret>c[i]){
			ret=c[i];
			t_ids=d[i];
		}
	if(t_ids!=-1){
 		edge[e].u=ids;
		edge[e].v=t_ids;
		edge[e++].w=ret-val;
	}
}

void update(int ids,int pos,int val)
{
	pos+=1000;
	for(int i=pos;i;i-=lowbit(i))
		if(val<c[i]){
			c[i]=val;
			d[i]=ids;
		}
}

void deal()
{
	memset(c,63,sizeof(c));
	sort(p,p+n,cmp_p);	
	for(int i=n-1;i>=0;--i){
		int pos=p[i].y-p[i].x;
		int val=p[i].y+p[i].x;
		query(p[i].ids,pos,val);
		update(p[i].ids,pos,val);
	}
}
int find(int x)
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}

int solve()
{
	if(n==k)
		return 0;
	e=0;
	deal();
	for(int i=0;i<n;++i)
		swap(p[i].x,p[i].y);
	deal();
	for(int i=0;i<n;++i)
		p[i].y=-p[i].y;
	deal();
	for(int i=0;i<n;++i)
		swap(p[i].x,p[i].y);
	deal();
	for(int i=0;i<n;++i) fa[i]=i;
	sort(edge,edge+e,cmp_e);
	for(int i=0;i<e;++i){
		int pa=find(edge[i].u);
		int pb=find(edge[i].v);
		if(pa!=pb){
			fa[pa]=pb;
			n--;
			if(n==k)
				return edge[i].w;
		}
	}
}

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;++i){
		scanf("%d%d",&p[i].x,&p[i].y);
		p[i].ids=i;
	}
	printf("%d",solve());			
} 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值