距离:
定义:
欧几里得距离:
设 \(A(x,y),B(a,b)\) ,则公式为
\[|AB|=\sqrt{(x-a)^2+(y-b)^2} \]一般模型:计算两点间的线段的长度。
曼哈顿距离:
两个点的曼哈顿距离为它们横坐标之差的绝对值和纵坐标之差的绝对值之和。
设 \(A(x,y),B(a,b)\) ,则公式为
\[|AB|=|x-a|+|y-b| \]一般模型:网格图中一个点走向另一个点的最短距离。
切比雪夫距离:
两个点的切比雪夫距离为它们横坐标之差和纵坐标之差的绝对值的最大值
设 \(A(x,y),B(a,b)\) ,则公式为:
\[|AB|=max(|x-a|,|y-b|) \]一般模型:棋盘问题,坐标移动问题。
转换:
曼哈顿距离和切比雪夫距离可以相互转换
有一个结论:曼哈顿坐标系是通过切比雪夫坐标系旋转 \(45\) 度后,再缩小到原来的一半得到的.
用图理解一下:与原点距离为一的点构成的图形:
- 曼哈顿
- 切比雪夫:
曼到切:
将一个点 \((x,y)\) 的坐标变为 \((x+y,x-y)\) 后,原坐标系中的曼哈顿距离等于新坐标系中的切比雪夫距离。
这样转化之后坐标有可能变成负数,例如数据结构之类无法处理坐标为负的情况,应当把这些处理后的坐标都加上同样的值,变成非负数。
切到曼:
将一个点 \((x,y)\) 的坐标变成 \((\large\frac{x+y}{2},\frac{x-y}{2})\) 时,原坐标系的切比雪夫距离等于新坐标系中的曼哈顿距离。
发现会除以二,可能不为整数,所以应当一开始把处理前坐标都乘二。
转化过程:
曼哈顿距离的公式其实是可以化开的:
\[|AB|=|x_2-x_1|+|y_2-y_1| \] \[|AB|=max(x_2-x_1+y_2-y_1,x_1-x_2+y_2-y_1,x_2-x_1+y-1-y_2,x_1-x_2+y_1-y_2) \] \[|AB|=max(|(x_2+y_2)-(x_1+y_1)|,|(x_2-y_2)-(x_1-y_1)|) \]此时,切比雪夫距离公式代入,得到:
\[A=(x_1+y_1,x_1-y_1),B=(x_2+y_2,x_2-y_2) \]同样的,也可以转化回去.
运用:
切比雪夫距离在计算的时候需要取 \(max\),往往不是很好优化,对于一个点,计算其他点到该的距离的复杂度为 \(O(n)\)
而曼哈顿距离只有求和以及取绝对值两种运算,我们把坐标排序后可以去掉绝对值的影响,进而用前缀和优化,可以把复杂度降为 \(O(1)\)
切比雪夫形成的图形都是正方形,比较好运用数据结构维护,而曼哈顿距离形成的图形为菱形,不好维护。
总之,两者之间需要灵活转化运用。
例题:
[USACO04OPEN]Cave Cows 3
题意:
给 \(n\) 个点的坐标,求这些点中随意两点曼哈顿距离的最大值
分析:
一开始单独用曼哈顿距离肯定不好做,因为曼哈顿不只是看最大和最小值。
考虑转化为 切比雪夫距离
然后就可以发现:直接算最大 \(x/y\) 和 最小 \(x/y\) 坐标之差的最大值就是答案了。
代码:
//P5098 [USACO04OPEN]Cave Cows 3
#include<bits/stdc++.h>
using namespace std;
int n;
int minx=1e9,miny=1e9,maxx,maxy;
int main(){
cin>>n;
for(int i=1,a,b,x,y;i<=n;i++){
scanf("%d%d",&a,&b); x=a+b,y=a-b;
minx=min(minx,x); miny=min(miny,y);
maxx=max(maxx,x); maxy=max(maxy,y);
}
cout<<max(maxx-minx,maxy-miny)<<endl;
system("pause");
return 0;
}
[TJOI2013]松鼠聚会
题意:
给定 \(n\) 个点,求其他 \(n-1\) 个点到 \(i\) 点的切比雪夫距离之和最小值。
分析:
首先,肯定要枚举每个点,但是切比雪夫距离无法求前缀和之类的东西(因为距离与横纵坐标的关系相关,不能分离来算)
考虑转化成曼哈顿距离(注意乘二),然后把计算 \(x\) 坐标之和和 \(y\) 坐标之和分离。
然后考虑枚举到第 \(i\) 个点的时的 \(x\) 值答案:
\[ansx=k_1x_i-\sum_{i=1}^{k_1} x_i+\sum_{i=k_1+1}^{n}x_i-(n-k_1)x_i \]此时, \(k\) 为所有节点的 \(x\) 节点小于 \(x_i\) 的值,\(sum\) 为横坐标递增排序后的前缀和。
因为分离了 \(x,y\) ,所以计算 \(y\) 值答案的公式也是一样的:
\[ansy=k_2y_i-\sum_{i=1}^{k_2} y_i+\sum_{i=k_2+1}^n y_i-(n-k_2)y_i \]答案就是加和的最小值,注意最后答案除以二。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,ans=1e17;
int x[N],y[N],p[N],q[N];
int sx[N],sy[N];
signed main(){
cin>>n;
for(int i=1,a,b;i<=n;i++){//转换为二倍的曼哈顿距离
scanf("%lld%lld",&a,&b); x[i]=p[i]=a+b,y[i]=q[i]=a-b;
}
sort(p+1,p+n+1); sort(q+1,q+n+1);
for(int i=1,x,y;i<=n;i++) sx[i]=sx[i-1]+p[i],sy[i]=sy[i-1]+q[i];
for(int i=1,X,Y,ansx,ansy;i<=n;i++){
X=lower_bound(p+1,p+n+1,x[i])-p;
Y=lower_bound(q+1,q+n+1,y[i])-q;
ansx=X*x[i]-2*sx[X]+sx[n]-(n-X)*x[i];
ansy=Y*y[i]-2*sy[Y]+sy[n]-(n-Y)*y[i];
ans=min(ans,ansx+ansy);
}
cout<<ans/2<<endl;
system("pause");
return 0;
}
拓展:
其实,这种转化还可以拓展到三维上.....