POJ - 3241 Object Clustering

题意:

给定平面上的 n 个点,定义两点距离为曼哈顿距离,现要将点分成 k (k < n) 组,定义组内距离 d 为最大的两点距离,求 m i n { m a x { d 1 , d 2 , . . . , d k } } min\{max\{d_1, d_2, ... , d_k\}\} 。(n <= 1e4)

链接:

https://vjudge.net/problem/POJ-3241

解题思路:

题意转化为求曼哈顿距离最小生成树上的第 k 大边,即第 n - k 小边。
显然,我们无法将边都建出来。首先看最小生成树的一个性质:
对于图中的一个环,删去环上的最大边,不影响最小生成树。
故对于平面图上的任意三点连接成的三角形,可舍去其中最大的边。
更近一步,如图:(距离为默认为曼哈顿距离)
在这里插入图片描述
对每个点 A,划分出 8 个 45 度区域,图示为其中一个区域。对于该区域的任意点 B、C,有 m a x { A B , A C } > B C max\{AB, AC\} > BC ,证明略。因此,A 只与该区域距离 A 最近的点连边(图示中的红色边,图例中的黄色边会在枚举B、C时筛选)。由于边双向,故只需考虑四个区域,边数最多为 4n。
接下来考虑如何得到最近的点。对于点 A ( x 0 , y 0 ) A(x_0,y_0) ,在图示区域的点 B ( x , y ) B(x, y) 满足 y > = y 0 y >= y_0 y x < = y 0 x 0 y - x <= y_0 - x_0 ,二维偏序问题,用树状数组维护即可。

参考代码:

#include<cstdio>
#include<cmath>
#include<algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e4 + 5;
const int maxm = 1e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

struct Edge{
	int u, v, w;
	bool operator < (const Edge &o) const{
		return w < o.w;
	}
} ei[maxm];
struct Node{
	int x, y, p;
	bool operator < (const Node &o) const{
		return y == o.y ? x > o.x : y > o.y;
	}
} a[maxn], b[maxn];
int xi[maxn], tot;
int c[maxn], pi[maxn];
int pre[maxn];
int n, m, k;

#define lowb(x) ((x)&(-x))
void update(int x, int v, int p){

	while(x <= tot){

		if(v < c[x]) c[x] = v, pi[x] = p;
		x += lowb(x);
	}
}

int query(int x){

	int p = pi[x], ret = c[x];
	while(x){

		if(c[x] < ret) ret = c[x], p = pi[x];
		x -= lowb(x);
	}
	return ret != inf ? p : 0;
}

int fin(int x){

	return x == pre[x] ? x : pre[x] = fin(pre[x]);
}

int kruskal(){

	for(int i = 1; i <= n; ++i) pre[i] = i;
	sort(ei + 1, ei + 1 + m);
	int cnt = 0, lim = n - k, ret;
	for(int i = 1; i <= m && cnt < lim; ++i){

		int u = ei[i].u, v = ei[i].v, w = ei[i].w;
		u = fin(u), v = fin(v);
		if(u == v) continue;
		pre[u] = v, ++cnt, ret = w;
	}
	return ret;
}

int dis(int i, int j){

	return abs(a[i].x - a[j].x) + abs(a[i].y - a[j].y);
}

void build(){

	m = 0;
	for(int i = 1; i <= n; ++i) b[i] = a[i];
	for(int d = 0; d < 4; ++d){

		if(d == 1 || d == 3){

			for(int i = 1; i <= n; ++i) swap(b[i].x, b[i].y);
		}
		else if(d == 2){

			for(int i = 1; i <= n; ++i) b[i].x = -b[i].x;
		}
		tot = 0;
		for(int i = 1; i <= n; ++i) xi[++tot] = b[i].y - b[i].x;
		sort(b + 1, b + 1 + n);
		sort(xi + 1, xi + 1 + tot);
		tot = unique(xi + 1, xi + 1 + tot) - xi - 1;
		for(int i = 1; i <= tot; ++i) c[i] = inf;
		for(int i = 1; i <= n; ++i){

			int x = lower_bound(xi + 1, xi + 1 + tot, b[i].y - b[i].x) - xi;
			int p = query(x);
			if(p) ei[++m] = {b[i].p, p, dis(b[i].p, p)};
			update(x, b[i].y + b[i].x, b[i].p);
		}
	}
}

int main(){

//	ios::sync_with_stdio(0); cin.tie(0);
	while(scanf("%d%d", &n, &k) != EOF){

		for(int i = 1; i <= n; ++i) scanf("%d%d", &a[i].x, &a[i].y), a[i].p = i;
		build();
		int ret = kruskal();
		printf("%d\n", ret);
	}
	return 0;
}
发布了55 篇原创文章 · 获赞 0 · 访问量 1266

猜你喜欢

转载自blog.csdn.net/weixin_44059127/article/details/101847682