曼哈顿距离

定义

出租车几何或曼哈顿距离(Manhattan Distance)是由十九世纪的赫尔曼·闵可夫斯基所创词汇 ,是种使用在几何度量空间的几何学用语,用以标明两个点在标准坐标系上的绝对轴距总和。
两点在南北方向上的距离加上在东西方向上的距离d(i,j)=|X1-X2|+|Y1-Y2|

曼哈顿图R语言 曼哈顿函数_算法



数学性质

  1. 非负性:d(i,j)≥0 距离是一个非负的数值
  2. 同一性:d(i,i)= 0 对象到自身的距离为0
  3. 对称性:d(i,j)= d(j,i)距离是一个对称函数
  4. 三角不等式:d(i,j)≤d(i,k)+d(k,j)从对象i到对象j的直接距离不会大于途经的任何其余对象k的距离

曼哈顿距离解决什么问题呢?

曼哈顿距离可以代替一个广搜,不过这个广搜是有条件限制的:
①只能上、下、左、右四个方向进行移动(很多迷宫问题其实都有这个特性)
②只求两点之间最短路径的长度不求路径过程



例如

Description

在一个 N 行 M 列的字符网格上, 恰好有 2 个彼此分开的连通块。每个连通 块的一个格点与它的上、下、左、右的格子连通。如下图所示:

曼哈顿图R语言 曼哈顿函数_c++_02


现在要把这 2 个连通块连通, 求最少需要把几个’.’转变成’X’。上图的例子中, 最少只需要把 3个’.’转变成’X’。下图用’*’表示转化为’X’的格点。


曼哈顿图R语言 曼哈顿函数_曼哈顿图R语言_03

Input

第 1 行:2 个整数 N 和 M(1<=N,M<=50) 接下来 N 行,每行 M 个字符, ’X’表示属于某个连通块的格点,’.’表示不属于某 个连通块的格点

Output

第 1 行:1 个整数,表示最少需要把几个’.’转变成’X’

Sample Input

6 16

…XXXX…XXX…
…XXXX…XX…
.XXXX…XXX…
…XXXXX…
…XXX…

Sample Output

3

有两种解法,一种是先用 DFS 把两个联通块区分开来,然后用 BFS求最短距离。另一种就是笔者要讲的 DFS + 曼哈顿距离

代码如下

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=50;
char g[N][N];
struct Node
{
	int x,y;
}Edge;

vector<Node> dist[2];

int n,m,cnt;

int dx[]={0,1,0,-1},dy[]={1,0,-1,0};

void dfs(int a,int b)
{
	g[a][b] = '.';  //避免重复搜索
	Edge.x = a;
	Edge.y = b;
	dist[cnt].push_back(Edge);
	
	for(int i=0;i<4;i++)
	{
		int x = a + dx[i];
		int y = b + dy[i];
		
		if(x >= 0 && y >= 0 && x < n && y < m && g[x][y] == 'X')
			dfs(x, y);
	}
}
 
int main()
{
	scanf("%d%d",&n,&m);
	
	for(int i=0;i<n;i++)
		scanf("%s",g[i]);
		
	cnt=0;	
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			if(g[i][j] == 'X')  //搜到一个连通块
			{
				dfs(i,j);
				cnt ++;
			}

	int ans = 0x3f3f3f3f;
	for(int i=0;i<dist[0].size();i++)
	{
		for(int j=0;j<dist[1].size();j++)
		{
			ans = min(ans, abs(dist[0][i].x - dist[1][j].x) + abs(dist[0][i].y - dist[1][j].y) - 1);
		}
	}
	printf("%d\n",ans);
	
	return 0;
}

再来一道娱乐题
菱形

输入一个奇数 n,输出一个由 * 构成的 n 阶实心菱形。

输入格式

一个奇数 n。

输出格式

输出一个由 * 构成的 n 阶实心菱形。
具体格式参照输出样例。

数据范围

1≤n≤99

输入样例:

5

输出样例:

曼哈顿图R语言 曼哈顿函数_算法_04

#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
    int n;
    cin >> n;

    int sx = n / 2, sy = n / 2;

    for (int i = 0; i < n ; i ++ )
    {
        for (int j = 0; j < n; j ++ )
        {
            if ( abs(sx - i) + abs(sy - j) <= n / 2 ) cout << "*";
            else cout << " ";
        }
        cout << endl;    
    }

    return 0;
}