曼哈顿最小生成树:

Object Clustering POJ - 3241
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大的边,因为本题的坐标比较小,y-x不需要离散化。
直接求就好啦,利用树状数组维护大于等于当前点y-x的最小的x+y。
即倒序扫描数组,对于每个点y-x,就更新其前最小值。查询时往后查询。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=100100;
const int inf=0x3f3f3f3f;
const int add=1010;
int f[maxn];
int n,k;
int tot;

struct nn
{
    int x,y;
    int z;
    nn(){}
    nn(int a,int b,int c)
    {
        x=a,y=b,z=c;
    }
}t[maxn];

struct node
{
    int x,y;
    int i;
}p[maxn];


bool cmp(const nn &a,const nn &b )
{
    return a.z<b.z;
}

bool ccmp(const node &a,const node &b)
{
    if(a.x!=b.x) return a.x<b.x;
    return a.y<b.y;
}

int fi(int x)
{
    if(f[x]!=x)
        f[x]=fi(f[x]);
    return f[x];
}

int ku(void)
{
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
    }

    sort(t+1,t+tot+1,cmp);

    int cnt=0;
    for(int i=1;i<=tot;i++)
    {
        int xx=fi(t[i].x);
        int yy=fi(t[i].y);
        if(xx==yy) continue;
        f[xx]=yy;
        cnt++;
        if(cnt==n-k) return t[i].z;
    }

}


int pos[maxn],sum[maxn];

void change(int x,int minn,int id)
{
    x+=add;
    for(;x;x-=(x&-x))
    {
        if(sum[x]>minn)
        {
            sum[x]=minn;
            pos[x]=id;
        }
    }
}

int ask(int x,int minn,int s)
{
    x+=add;
    int ans=inf;
    int e=-1;
    for(;x<maxn;x+=(x&-x))
    {
        if(sum[x]<ans)
        {
            ans=sum[x];
            e=pos[x];
        }
    }
    if(e!=-1)
        t[++tot]=nn(s,e,ans-minn);
}

void build(void)
{
    sort(p+1,p+n+1,ccmp);
    memset(sum,0x3f,sizeof(sum));
    memset(pos,-1,sizeof(pos));

    for(int i=n;i>=1;i--)
    {
        int x=p[i].y-p[i].x;
        int minn=p[i].x+p[i].y;
        ask(x,minn,p[i].i);
        change(x,minn,p[i].i);

    }
}


int main(void)
{
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        tot=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&p[i].x,&p[i].y);
            p[i].i=i;
        }

        build();

        for(int i=1;i<=n;i++)
            swap(p[i].x,p[i].y);
        build();

        for(int i=1;i<=n;i++)
            p[i].x=-p[i].x;
        build();

        for(int i=1;i<=n;i++)
            swap(p[i].x,p[i].y);
        build();

        printf("%d\n",ku());
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值