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

2023-10-29 00:40

本文主要是介绍POJ 3241 Object Clustering 曼哈顿最小生成树,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Object Clustering

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

 

整个题:就是求manhattan最小生成树

曼哈顿距离最小生成树问题可以简述如下:

给定二维平面上的N个点,在两点之间连边的代价为其曼哈顿距离,求使所有点连通的最小代价。

朴素的算法可以用O(N2)的Prim,或者处理出所有边做Kruskal,但在这里总边数有O(N2)条,所以Kruskal的复杂度变成了O(N2logN)。

但是事实上,真正有用的边远没有O(N2)条。我们考虑每个点会和其他一些什么样的点连边。可以得出这样一个结论,以一个点为原点建立直角坐标系,在每45度内只会向距离该点最近的一个点连边。

这个结论可以证明如下:假设我们以点A为原点建系,考虑在y轴向右45度区域内的任意两点B(x1,y1)和C(x2,y2),不妨设|AB|≤|AC|(这里的距离为曼哈顿距离),如下图:

 

|AB|=x1+y1,|AC|=x2+y2,|BC|=|x1-x2|+|y1-y2|。而由于B和C都在y轴向右45度的区域内,有y-x>0且x>0。下面我们分情况讨论:

1.      x1>x2且y1>y2。这与|AB|≤|AC|矛盾;

2.      x1≤x2且y1>y2。此时|BC|=x2-x1+y1-y2,|AC|-|BC|=x2+y2-x2+x1-y1+y2=x1-y1+2*y2。由前面各种关系可得y1>y2>x2>x1。假设|AC|<|BC|即y1>2*y2+x1,那么|AB|=x1+y1>2*x1+2*y2,|AC|=x2+y2<2*y2<|AB|与前提矛盾,故|AC|≥|BC|;

3.      x1>x2且y1≤y2。与2同理;

4.      x1≤x2且y1≤y2。此时显然有|AB|+|BC|=|AC|,即有|AC|>|BC|。

综上有|AC|≥|BC|,也即在这个区域内只需选择距离A最近的点向A连边。

这种连边方式可以保证边数是O(N)的,那么如果能高效处理出这些边,就可以用Kruskal在O(NlogN)的时间内解决问题。下面我们就考虑怎样高效处理边。

我们只需考虑在一块区域内的点,其他区域内的点可以通过坐标变换“移动”到这个区域内。为了方便处理,我们考虑在y轴向右45度的区域。在某个点A(x0,y0)的这个区域内的点B(x1,y1)满足x1≥x0且y1-x1>y0-x0。这里对于边界我们只取一边,但是操作中两边都取也无所谓。那么|AB|=y1-y0+x1-x0=(x1+y1)-(x0+y0)。在A的区域内距离A最近的点也即满足条件的点中x+y最小的点。因此我们可以将所有点按x坐标排序,再按y-x离散,用线段树或者树状数组维护大于当前点的y-x的最小的x+y对应的点。时间复杂度O(NlogN)。

至于坐标变换,一个比较好处理的方法是第一次直接做;第二次沿直线y=x翻转,即交换x和y坐标;第三次沿直线x=0翻转,即将x坐标取相反数;第四次再沿直线y=x翻转。注意只需要做4次,因为边是双向的。

至此,整个问题就可以在O(NlogN)的复杂度内解决了。

  

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include<vector>
#include <algorithm>
using namespace std;
const int N = 1e4+20, M = 4e4+10, mod = 1e9+7, inf = 0x3f3f3f3f;
typedef long long ll;struct ss{int u,v,w;bool operator < (const ss &b) const{return w < b.w;}
}e[N * 4];
int tot = 0 ,id[N] , mi[N] , pos[N] , x[N], cnt ,  y[N], san[N], fa[N], n ,k;
bool cmp(int i,int j)
{if(x[i] != x[j]) return x[i] < x[j];else return y[i] < y[j];
}
void add(int u,int v,int w) {++tot;e[tot].u = u, e[tot].v = v, e[tot].w = w;
}
int query(int x) {int ret = -1, ans = inf;for(int i = x; i <= cnt; i += i&(-i)) {if(mi[i] < ans) ans = mi[i] , ret = pos[i];}return ret;
} 
void update(int x, int c, int p) {for(int i = x; i >= 1; i -= i&(-i)) if(mi[i] > c) mi[i] = c, pos[i] = p;
}int haxi(int x) {return lower_bound(san + 1, san + cnt + 1, x) - san;}
int dis(int i,int j)
{return abs(x[i] - x[j]) + abs(y[i] - y[j]);
}
void Manst() {tot = 0;for(int dir = 0; dir < 4; ++dir) {if(dir == 1 || dir == 3)for(int i = 1; i <= n; ++i) swap(x[i],y[i]);elsefor(int i = 1; i <= n; ++i) x[i] = -x[i];for(int i = 1; i <= n; ++i) id[i] = i;sort(id + 1, id + n + 1, cmp);cnt = 0;for(int i = 1; i <= n; ++i) san[++cnt] = y[i] - x[i];sort(san + 1, san + cnt + 1);cnt = unique(san + 1, san + cnt + 1) - san - 1;for(int i = 1; i <= n; ++i) mi[i] = inf , pos[i] = -1;for(int i = n; i >= 1; --i) {int u = haxi(y[id[i]] - x[id[i]]);int v = query(u);if(v != -1) add(id[i], v, dis(id[i], v));update(u, x[id[i]] + y[id[i]], id[i]);}}
}int finds(int x) {return x==fa[x]?x:fa[x]=finds(fa[x]);}int main()
{while(~scanf("%d%d",&n,&k)) {for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);Manst();sort(e + 1, e+ tot + 1);for(int i = 1; i <= n; ++i) fa[i] = i;k = n - k;for(int i = 1; i <= tot; ++i){   int u = e[i].u, v = e[i].v, c = e[i].w;if(finds(u) != finds(v)) {--k;fa[finds(u)] = finds(v);if(k ==  0) {printf("%d\n",c);break;}}}}
}

 

转载于:https://www.cnblogs.com/zxhl/p/5707999.html

这篇关于POJ 3241 Object Clustering 曼哈顿最小生成树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/297072

相关文章

详解Java中如何使用JFreeChart生成甘特图

《详解Java中如何使用JFreeChart生成甘特图》甘特图是一种流行的项目管理工具,用于显示项目的进度和任务分配,在Java开发中,JFreeChart是一个强大的开源图表库,能够生成各种类型的图... 目录引言一、JFreeChart简介二、准备工作三、创建甘特图1. 定义数据集2. 创建甘特图3.

深入探讨Java 中的 Object 类详解(一切类的根基)

《深入探讨Java中的Object类详解(一切类的根基)》本文详细介绍了Java中的Object类,作为所有类的根类,其重要性不言而喻,文章涵盖了Object类的主要方法,如toString()... 目录1. Object 类的基本概念1.1 Object 类的定义2. Object 类的主要方法3. O

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

poj 1511 Invitation Cards(spfa最短路)

题意是给你点与点之间的距离,求来回到点1的最短路中的边权和。 因为边很大,不能用原来的dijkstra什么的,所以用spfa来做。并且注意要用long long int 来存储。 稍微改了一下学长的模板。 stack stl 实现代码: #include<stdio.h>#include<stack>using namespace std;const int M

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];