「BZOJ1604」[Usaco2008 Open] Cow Neighborhoods 奶牛的邻居

本文介绍了一种将曼哈顿距离转换为切比雪夫距离的算法,并结合并查集和平衡树解决奶牛群划分问题。通过将距离问题转化为切比雪夫距离,利用平衡树维护奶牛位置关系,实现高效计算。

算法:曼哈顿距离转切比雪夫距离+并查集+平衡树

(这里的代码选择STL里的set实现平衡树)

储备知识

曼哈顿距离

在二维空间内,两个点之间的曼哈顿距离为它们横坐标之差的绝对值与纵坐标之差的绝对值之和。设点 A(x1,y1),B(x2,y2),则 A,B 之间的曼哈顿距离用公式可以表示为:
dAB=|x1-x2|+|y1-y2|
在这里插入图片描述

通过公式,我们很容易得到 A,B两点间的曼哈顿距离:
dAB=|6-2|+|5-2|=4+3=7

切比雪夫距离

在二维空间内,两个点之间的切比雪夫距离为它们横坐标之差的绝对值与纵坐标之差的绝对值的最大值。设点 A(x1,y1),B(x2,y2),则 A,B 之间的切比雪夫距离用公式可以表示为:
dAB=max(|x1-x2|,|y1-y2|)

在这里插入图片描述
通过公式,我们很容易得到 A,B两点间的切比雪夫距离:
dAB=max(|6-2|,|5-2|)=max(4,3)=4

二维曼哈顿距离与切比雪夫距离的相互转化

设A(x1,Y1),B(x2,y2),A,B两点的曼哈顿距离为:
dAB=|x1-x2|+|y1-y2|
=max{x1-x2+y1-y2,x1-x2+y2-y1,x2-x1+y1-y2,x2-x1+y2-y1}
=max{|(x1+y1)-(x2+y2)|,|(x1-y1)-(x2-y2)|}

很容易发现,这就是 (x1 + y1,x1- y1), (x2 + y2,x2 - y2) 两点之间的切比雪夫距离。
这样,我们就把两点间的曼哈顿距离转化成了切比雪夫距离。

set

STL中很重要的一种容器!!!不会的自行百度学习!!!

题目

题面描述

了解奶牛们的人都知道,奶牛喜欢成群结队.观察约翰的N(1≤N≤100000)只奶牛,你会发现她们已经结成了几个“群”.
每只奶牛在吃草的时候有一个独一无二的位置坐标Xi,Yi(l≤Xi,Yi≤[1~109];Xi,Yi∈整数.
当满足下列两个条件之一,两只奶牛i和j是属于同一个群的:
1.两只奶牛的曼哈顿距离不超过C(1≤C≤109),即lXi - Xjl+IYi- Yjl≤C.
2.两只奶牛有共同的邻居.即,存在一只奶牛k,使i与k,j与k均同属一个群.
给出奶牛们的位置,请计算草原上有多少个牛群,以及最大的牛群里有多少奶牛?

输入
第1行:N和C;
接下来N行:每行输入一只奶牛的坐标.
输出
输出仅一行,先输出牛群数,再输出最大牛群里的牛数,用空格隔开.

解题思路

先将A(x1,Y1),B(x2,y2)两点的曼哈顿距离:|x1-x2|+|y1-y2|按上述知识转化为 (x1 + y1,x1- y1), (x2 + y2,x2 - y2) 两点之间的切比雪夫距离:max{|(x1+y1)-(x2+y2)|,|(x1-y1)-(x2-y2)|}。
设X=x+y,Y=x-y,现在只要保证max{△X,△Y}<C,既保证△X<C且△Y<C即可。
先将转化后的所有点按X排序,维护△X<c,不满足则指针now右移。然后对Y维护平衡树,如果当前点与其前驱后继的△Y<C,则用并查集将它们合并在一起(即放在一个牛群中)。
具体细节请看代码中注释!!!

代码演示
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#define inf 10000000000LL
using namespace std;
int n,c,ans,maxn;
int fa[100005],tot[100005];
struct data
{
    long long x,y; //x即为思路中的X,y即为思路中的Y
    int id;
}a[100005];
multiset <data> b;
set <data>::iterator it;
inline bool operator<(data a,data b) //重载<
{
    return a.y<b.y;
}
inline bool cmpx(data a,data b)
{
    return a.x<b.x;
}
int read() //快读
{
    long long x=0,f=1;
    char ch=getchar();
    if (ch=='-') f=-1;
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9')
    {
        x=(x*10+ch-'0');
        ch=getchar();
    }
    return x*f;
}
int find(int x)
{
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline void un(int x,int y) //并查集合并
{
    int p=find(x),q=find(y);
    if(p!=q)
    {
        fa[p]=q;
        ans--; //如果两头牛在一个群里,牛群数-1
    }
}
int main()
{
    n=read();
    c=read();
    ans=n; //开始设有n个牛群
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=n;i++)
    {
        int t1=read(),t2=read();
        a[i].x=t1+t2;
        a[i].y=t1-t2; //曼哈顿距离转切比雪夫距离
        a[i].id=i;
    }
    sort(a+1,a+n+1,cmpx); //所有牛按X排序
    b.insert((data){0,inf,0});
    b.insert((data){0,-inf,0});   
    int now=1;
    b.insert(a[1]);
    for(int i=2;i<=n;i++)
    {
        while(a[i].x-a[now].x>c) //保证同一牛群的牛△X<C,不满足则指针now右移
        {
            b.erase(b.find(a[now]));
            now++;
        }
        it=b.lower_bound(a[i]);
        data r=*it,l=*--it; //找按X排序后当前牛的前驱后继的位置
        if(a[i].y-l.y<=c) un(a[i].id,l.id); 
        if(r.y-a[i].y<=c) un(a[i].id,r.id); //如果前驱(后继)与当前牛的△Y<C
        b.insert(a[i]);
    }
    for(int i=1;i<=n;i++) tot[find(i)]++;
    //根节点相同的牛在同一并查集中,统计每个牛群含牛头数
    for(int i=1;i<=n;i++) maxn=max(maxn,tot[i]);
    printf("%d %d\n",ans,maxn);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值