平面点曼哈顿最小生成树——POJ 3241 Object Clustering

对应POJ题目:点击打开链接

Object Clustering

Time Limit: 2000MS   Memory Limit: 131072K
Total Submissions: 1697   Accepted: 418

Description

We have (N ≤ 10000) objects, and wish to classify them into several groups by judgement of their resemblance. To simply the model, each object has 2 indexes a and b (ab ≤ 500). The resemblance of
object i and object j is defined by dij = |a- aj| + |b- bj|, and then we say i is dij resemble to j.
Now we want to find the minimum value of X, so that we can classify the N objects into K (< N) groups, and in each group, one object is at most X resemble to another object in the same group, i.e, for every object i,
if i is not the only member of the group, then there exists one object j (i ≠ j) in the same group that satisfies dij ≤ X

Input

The first line contains two integers N and K. The following N lines each contain two integers a and b, which describe a object.

Output

A single line contains the minimum X.

Sample Input

6 2
1 2
2 3
2 2
3 4
4 3
3 1

Sample Output

2

题意:

题目很短,然而我并没有看懂,直接搜了下题意:求平面曼哈顿最小生成树第k大的边的长度。

思路:

其实是在做线段树离线处理的题目无意中注意到莫队算法(据说可以在O(n^1.5)内解决几乎所有无修改的区间查询问题),而且需要有平面曼哈顿最小生成树为前提知识~故来A此题。

平面曼哈顿最小生成树:点击打开链接 ,极为详细!

大概知道怎样做之后,来看看细节:对所有点(x, y),以x为第一关键字,y为第二关键字升序排序,然后从后面往前处理每个点(假设为p(x, y))的R1域,怎样寻找p点R1域里跟p点最近的点呢?假设(xi, yi)为R1域的一点,论文里也讲到需要满足x >= xi  &&  yi - xi >= y - x;因为是从后往前处理,所以x >= xi是肯定满足的;所以我们需要维护区间[y-x,
 MAX[y-x]](MAX[y-x]表示y-x可能的最大值(因为y-x可能为负数,所以是加上了偏移量的最大值))里面x+y的最小值以及那个点的编号;这里可以用二叉排序树,线段树,树状数组。

在连好每个点的R1域后,我们需要连完每个点的其他7个域,因为边是无向的,所以如果a点的R1域连的是b,那b点的R5域也是a,其他类似;那就没必要把8个域都做一遍,只需要做R1~R4域就行了,虽然论文讲R1跟R2可以一起做,但我觉得有不妥~所以还是把4个域分别做一遍。完成R1域后,其他3个域可以根据坐标翻转性质把他们翻到R1域,然后使用相同的算法来做。

怎样旋转呢?连好R1域后,把所有点按直线y = x翻转(此时初始的R2域的到了R1域,初始的R3域的到了R8域,初始的R4域的到了R7域),就可以求R2域了;再把所有点按直线x
= 0翻转(此时初始的R3域(之前在R8域)的到了R1域,初始的R4域(之前在R7)的到了R2域),就可以求R3域了;再把所有点按直线y = x翻转(此时初始的R4域(之前在R2域)的到了R1域,就可以求R4域了

最后到了熟悉的kruskal算法求最小生成树~

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define N 10010
#define MAX_SIZE 2001 //y-x最大值
#define Right_go 1001 //右移偏移量
#define MIN(x, y) ((x)<(y)?(x):(y))
#define inf 0x7fffffff
using namespace std;
int Size; //总边数(可能含重复边)
int fa[N]; //并查集
int a[MAX_SIZE+5]; //a[i] = a[y-x];
int ID[MAX_SIZE+5]; //ID[i] = ID[y-x],即表示(x, y)的结点编号
//树状数组维护的值c[i]为c[x+y]最小值,CID[x+y]为(x, y)的结点编号
int c[MAX_SIZE+5];
int CID[MAX_SIZE+5];

typedef struct{
	int x, y, id;
}Point;

Point p[N];

bool cmp(Point p1, Point p2)
{
	return p1.x < p2.x;
}

typedef struct{
	int u, v, w;
}Edge;

Edge E[10*N];

void Add(int u, int v, int w)
{
	Size++;
	E[Size].u = u;
	E[Size].v = v;
	E[Size].w = w;
}

void Init()
{
	int i;
	for(i = 1; i <= MAX_SIZE; i++){
		a[i] = c[i] = inf;
		ID[i] = CID[i] = -1;
	}
}

inline int lowbit(int x)
{
	return x & (-x);
}

void Update(int pos, int val, int id)
{
	int i;
	if(val >= a[pos]) return;
	a[pos] = val;
	ID[pos] = id;
	for(i = pos; i <= MAX_SIZE; i += lowbit(i))
		if(val < c[i]){
			c[i] = val;
			CID[i] = id;
		}
}

void Query(int l, int r, int i)
{
    int ans = inf, id = -1;
    while(true){
		if(a[r] < ans){
			ans = a[r];
			id = ID[r];
		}
        if(r == l) break;
        for(r -= 1; r - l >= lowbit(r); r -= lowbit(r))
			if(c[r] < ans){
				ans = c[r];
				id = CID[r];
			}
    }
	if(ans != inf) Add(id, p[i].id, ans - (p[i].y + p[i].x));
}

void Cal(int n)
{
	sort(p + 1, p + n + 1, cmp);
	int i;
	Init();
	for(i = n; i > 0; i--){
		int pos = p[i].y - p[i].x;
		pos += Right_go;
		Query(pos, MAX_SIZE, i);
		Update(pos, p[i].y + p[i].x, p[i].id);
	}
}

void Swap(int &a, int &b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

void Solve(int n)
{
	Cal(n);
	int j;
	//每个点坐标沿y = x直线翻转
	for(j = 1; j <= n; j++)
		Swap(p[j].x, p[j].y);
	Cal(n);
	//每个点坐标沿x = 0直线翻转
	for(j = 1; j <= n; j++)
		p[j].x = -p[j].x;
	Cal(n);
	//每个点坐标沿y = x直线翻转
	for(j = 1; j <= n; j++)
		Swap(p[j].x, p[j].y);
	Cal(n);
}

bool cmpk(Edge e1, Edge e2)
{
	return e1.w < e2.w;
}

int find(int x)
{
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}  

void kruskal(int n, int k)
{
	sort(E + 1, E + Size + 1, cmpk);
	int cnt = 1, j = 1;
    int u1, v1, c1, c2;
    for(int i = 1; i <= n; i++) fa[i] = i;
    while(cnt <= n - k)
    {
        u1=E[j].u;
        v1=E[j].v;
        c1=find(u1);
        c2=find(v1);
        if(c1!=c2){//u1与v1属于不同的集合
            fa[c1]=c2;//合并
			cnt++;
        }
        j++;
    }
    printf("%d\n", E[j-1].w);
}

int main()
{
	//freopen("in.txt","r",stdin);
	int n, k;
	while(scanf("%d%d", &n, &k) == 2){
		Size = 0;
		int i, j;
		for(i = 1; i <= n; i++){
			scanf("%d%d", &p[i].x, &p[i].y);
			p[i].id = i;
		}
		Solve(n);
		kruskal(n, k);
	}
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-05 21:45:44

平面点曼哈顿最小生成树——POJ 3241 Object Clustering的相关文章

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

题意: 平面上有n个点,现在把他们分成k个集合,使得每个集合中的每个点都至少有一个本集合的点之间的曼哈顿距离不大于X,求最小的X. 分析: 转化为求n个点生成完全图的最小生成树的第k大边.接下来有几个重点. 1)根据莫队算法,由于边权是点的曼哈顿距离,每个点只需要跟周围8个方向中每个方向最近的点连边,这样算出的图与用完全图算出的最小生成树一样,涉及的边却大大减少. 2)用树状数组维护y右偏45度的最近点,每个点以y-x的位置,y+x的值放入树状数组,由于每次是查询区间(pos,last)的情况,

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

Object Clustering Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 2640   Accepted: 806 Description We have N (N ≤ 10000) objects, and wish to classify them into several groups by judgement of their resemblance. To simply the model, each

POJ 3241 Object Clustering 莫队算法

第n-k大曼哈顿距离,莫队算法裸题 Object Clustering Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 1584   Accepted: 366 Description We have N (N ≤ 10000) objects, and wish to classify them into several groups by judgement of their resemblance. To simp

POJ 3241 Object Clustering 二维平面曼哈顿距离最小生成树

题目链接:点击打开链接 题意: 给定二维平面上的n个点坐标,常数k 下面n行给出坐标 求一个最小生成树,问第k大的边是多少. 任意两个点间建一条边的花费是其曼哈顿距离. 思路:转自:点击打开链接 一.曼哈顿距离最小生成树 曼哈顿距离最小生成树问题可以简述如下: 给定二维平面上的N个点,在两点之间连边的代价为其曼哈顿距离,求使所有点连通的最小代价. 朴素的算法可以用O(N2)的Prim,或者处理出所有边做Kruskal,但在这里总边数有O(N2)条,所以Kruskal的复杂度变成了O(N2logN

POJ 3241 Object Clustering 曼哈顿距离最小生成树

题目大意:求出曼哈顿距离最小生成树上的第k大边权. 思路:首先,你要了解:http://blog.csdn.net/acm_cxlove/article/details/8890003 也就是说,我们以每一个点为中心,把平面分成8个部分,每一个部分我们只需要离这个点最近的点.然后加上建一条边连接这个边和最近的点.然后就是MST. 听说这个算法是莫队算法的基础,我现在就去学. CODE: #include <cstdio> #include <cstring> #include &l

POJ 3241 Object Clustering(Manhattan MST)

题目链接:http://poj.org/problem?id=3241 Description We have N (N ≤ 10000) objects, and wish to classify them into several groups by judgement of their resemblance. To simply the model, each object has 2 indexes a and b (a, b ≤ 500). The resemblance of ob

曼哈顿最小生成树

1.poj 3241 Object Clustering 题意:平面上有n个点,点之间的距离采用曼哈顿距离衡量.求一个最小距离X,使得在把这些点分为k组后,每组中两两点之间的距离不超过X. 思路:首先我们这么想,如果所有点都在1个组中,即k=1时,那么所要求的X即为该n个点的曼哈顿最小生成树的最大边:当k=2时,如果我们将最小生成树的最大边割开,形成2组,答案仍未原最小生成树的第2大的边(不会比之还小)……以此类推,可转换为求解平面上n个点的曼哈顿距离最小生成树的第k大的边的长度. 接下来,就是

POJ3241 Object Clustering 曼哈顿最小生成树

题意:转换一下就是求曼哈顿最小生成树的第n-k条边 参考:莫涛大神的论文<平面点曼哈顿最小生成树> /* Problem: 3241 User: 96655 Memory: 920K Time: 94MS Language: C++ Result: Accepted */ #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cs

【POJ 3241】曼哈顿最小生成树(模板整理)

关于 曼哈顿最小生成树 的证明见:http://www.2cto.com/kf/201505/399861.html 模板: #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 100010; const int INF = 0x3f3f3f3f; struct Point{ int x,y,i