对应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
#include
#include
#include
#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;
}