曼哈顿距离

我们通常所指的距离是欧拉距离,这种距离体系很好的满足了三角形不等式,也合理地体现了空间中距离的大小关系。但是这一类距离在某些场合,甚至实际生活中却不太适用。


如图为美国纽约曼哈顿,在这里,距离的定义大概是 $dis(A,B)=\abs{A_x-B_x}+\abs(A_y-B_y)$,因为你只能沿着横平竖直的马路行进。曼哈顿距离的名称也从此而来。
衡量一个距离的定义是否合理,我们一般要检测它是否满足三角形不等式,如:
对于欧拉距离:$$dis(A,B)=\sqrt{(A_x-B_x)^2+(A_y-B_y)^2}<=dis(A,C)+dis(B,C)$$
对于曼哈顿距离:$$dis(A,B)=|A_x-B_x|+|A_y-B_y|<=dis(A,C)+dis(B,C)$$
现在我们已经确定了这种距离的定义是合理的,但是还要注意,
在考虑曼哈顿距离的同时,我们需要注意一下几点:
1. 欧拉距离在进行了坐标系的线性变换之后依然不变,但是曼哈顿距离却会随坐标轴的方向变化。几种情况例外,就是当反转 x 轴或 y 轴,以及交换 x,y 轴时。
2. 到某个点曼哈顿距离相同的点的集合是一个斜的正方形。这个结论在思考和分析过程中非常有用。

曼哈顿距离生成树

对于平面上的两个点,如果用最短的平行于坐标轴的折线连接,折线的长度就是这两个点的曼哈顿距离。
对于平面上的多个点,选取适当的点以上述折线连接,使得所有点联通,构成这些点的曼哈顿距离生成树。
折线总长度最小的曼哈顿距离生成树叫做曼哈顿距离最小生成树。

如何求出曼哈顿距离生成树呢?

最简单的想法是:先将平面上的 n 个点暴力连边,再用 kruskal 算法求解。时间复杂度约 $O(n^2log(n))$
注意到这种算法将许多没有意义的边连接了,所以我们需要分析如何才能高效地连接有意义的边。

引理 1:对于一个点,我们只需要连接每个 1/8 象限内的离它最近的点。

需要证明若 A 为离 O 最近的点,连边 (O,A),(A,B) 比 (O,A),(O,B) 优,同时对于 C 也类似。
已知:$A_x+A_y<B_x+B_y$
需要证明:$(O,A)+(A,B)<(O,A)+(O,B)$
证:
因为:
$$
(O,A)+(A,B)=A_x+A_y+|B_x-A_x|+|B_y-A_y|
$$
$$
(O,A)+(O,B)=A_x+A_y+B_x+B_y
$$
所以只需证
$$
|B_x-A_x|+|B_y-A_y|<B_x+B_y
$$
拆开绝对值符号分别得到:
$$
-A_x-A_y<0
$$
$$
A_y-A_x<2B_y
$$
$$
A_x-A_y<2B_x
$$
$$
A_x+A_y<2B_x+2B_y
$$
分别都可以证明是对的。其他象限也可以如此证明。
因此我们已经证明了引理 1,就可以用它来建立生成树了。

寻找最近点

如何寻找最近的点成为了一个问题。如果我们遍历所有的点,与之前的暴力连边没有什么改进。因此我们会借助一定的遍历次序,和优秀的数据结构。

对于如图的 1/8 象限内的所有点 A, 离 O 最近的点一定是 $A_x+A_y$最小的那一个。为了找到那一个点,我们需要限制一下几个条件:
$$
A_y-A_x>=0
$$
$$
X>=0
$$
对于第一个条件,我们可以用线段树的区间表示,线段树的每一个区间 [a,b] 表示当前 A_y-A_x∈[a,b] 的所有点中 A_x+A_y 最小的一个。
对于第二个条件,我们可以按照 X 降序遍历。
至此,我们解决了所有问题,曼哈顿距离最小生成树问题也初步得到了解决。

扩展:

1. 如果点的范围比较大,需要进行离散化。
2. 对于动态更新的类似问题,请参考 http://www.doc88.com/p-998527952232.html

题目

一道裸题:51NOD1213 求曼哈顿距离最小生成树的边长度之和。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>

#define MX 50005
#define MT 2000010
#define ME 1000005
#define MOV 1000001
#define oo 90000000
#define ls (node<<1)
#define rs (node<<1|1)
#define mid ((l+r)>>1)
#define dis(a,b) (abs((a).x-(b).x)+abs((a).y-(b).y))

using namespace std;

typedef struct point
{
    int x,y,id;
}points;

typedef struct tnode
{
    int mn,id;
}segtre;

points pot[MX];
segtre tre[MT<<2];
int fst[MX],nxt[ME],u[ME],v[ME],w[ME],lnum;
int fa[MX];
int ord[ME];
int n;

inline void addeg(int nu,int nv,int nw)
{
    nxt[++lnum]=fst[nu];
    fst[nu]=lnum;
    u[lnum]=nu;
    v[lnum]=nv;
    w[lnum]=nw;
}

int findfa(int x)
{
    return x==fa[x]?x:fa[x]=findfa(fa[x]);
}

inline void upd(int node)
{
    if(tre[ls].mn<tre[rs].mn)tre[node].mn=tre[ls].mn,tre[node].id=tre[ls].id;
    else tre[node].mn=tre[rs].mn,tre[node].id=tre[rs].id;
}

void build(int node,int l,int r)
{
    tre[node].mn=+oo;
    tre[node].id=-1;
    if(l!=r)build(ls,l,mid),build(rs,mid+1,r);
}

void cng(int l,int r,int node,int p,int x,int id)
{
    if(l==r)tre[node].mn=x,tre[node].id=id;
    else if(p<=mid)cng(l,mid,ls,p,x,id),upd(node);
    else cng(mid+1,r,rs,p,x,id),upd(node);
}

segtre qmn(int l,int r,int node,int ql,int qr)
{
    segtre nul,a,b;
    nul.mn=+oo;
    nul.id=-1;
    if(ql<=l&&r<=qr)return tre[node];
    else if(ql>r||qr<l)return nul;
    else
    {
        a=qmn(l,mid,ls,ql,qr);
        b=qmn(mid+1,r,rs,ql,qr);
        if(a.mn<b.mn)return a;
        else return b;
    }
}

inline bool cmpx(points a,points b)
{
    return (a.x!=b.x)?(a.x>b.x):a.y>b.y;
}

inline bool cmpe(int a,int b)
{
    return w[a]<w[b];
}

inline void sector1()
{
    segtre near;
    for(int i=1;i<=n;i++)
    {
        near=qmn(1,MOV<<1,1,MOV+pot[i].y-pot[i].x,+oo);
        if(near.mn==+oo||near.id==-1);
        else
        {
            addeg(pot[i].id,near.id,near.mn-pot[i].x-pot[i].y);
            addeg(near.id,pot[i].id,near.mn-pot[i].x-pot[i].y);
        }
        cng(1,MOV<<1,1,MOV+pot[i].y-pot[i].x,pot[i].x+pot[i].y,pot[i].id);
    }
}

long long cruskal()
{
    long long ans=0,cnt=0;
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=lnum;i++)ord[i]=i;
    sort(ord+1,ord+lnum+1,cmpe);
    for(int i=1;i<=lnum;i++)
    {
        if(findfa(u[ord[i]])!=findfa(v[ord[i]]))
        {
            ans+=(long long)w[ord[i]];
            cnt++;
            fa[findfa(u[ord[i]])]=findfa(v[ord[i]]);
        }
        if(cnt>=n-1)break;
    }
    return ans;
}

void work()
{   
    sort(pot+1,pot+n+1,cmpx);
    build(1,1,MOV<<1);
    sector1();

    for(int i=1;i<=n;i++)pot[i].x=-pot[i].x+MOV;
    sort(pot+1,pot+n+1,cmpx);
    build(1,1,MOV<<1);
    sector1();

    for(int i=1;i<=n;i++)swap(pot[i].x,pot[i].y);
    sort(pot+1,pot+n+1,cmpx);
    build(1,1,MOV<<1);
    sector1();

    for(int i=1;i<=n;i++)pot[i].y-=MOV,pot[i].y*=-1;
    sort(pot+1,pot+n+1,cmpx);
    build(1,1,MOV<<1);
    sector1();

    printf("%lld\n",cruskal());
}

void input()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&pot[i].x,&pot[i].y),pot[i].id=i;
}

int main()
{
    input();
    work();
    return 0;
}

玻璃钢生产厂家玻璃钢花盆儿榆树玻璃钢雕塑工程施工玻璃钢卡通雕塑3d雕塑佛山玻璃钢不锈钢雕塑玻璃钢造型雕塑有哪些济南清远玻璃钢动物雕塑红河玻璃钢卡通雕塑厂家南通人物玻璃钢雕塑安装山东佛像玻璃钢雕塑价位商丘佛像校园玻璃钢景观雕塑湛江喷泉雕塑玻璃钢东莞江西玻璃钢人物雕塑厂家玻璃钢花盆花箱款式多山西景区玻璃钢雕塑图片天津仿铜玻璃钢雕塑价位佛山玻璃钢酒瓶雕塑中秋节商场美陈怎么做宝山玻璃钢花盆周口景观玻璃钢雕塑公司四川步行街玻璃钢雕塑价位湖北玻璃钢果蔬雕塑动物玻璃钢雕塑定做玻璃钢雕塑用什么胶粘华阴玻璃钢卡通座椅雕塑上海大型玻璃钢雕塑质量材质上乘大型玻璃钢雕塑安徽步行街玻璃钢雕塑批发定做加工玻璃钢雕塑厂家商场美陈出租合浦玻璃钢泡沫雕塑厂家香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化