对应POJ题目:点击打开链接
Time Limit: 2000MS | Memory Limit: 131072K | |
Total Submissions: 1697 | Accepted: 418 |
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 object i and object j is defined by dij = |ai - aj| + |bi - 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 (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; }