【基础算法】最短距离总和问题

218 阅读3分钟

🌹作者:云小逸
📝个人主页:云小逸的主页
📝Github:云小逸的Github
🤟motto:要敢于一个人默默的面对自己,==强大自己才是核心==。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在!学会自己和解,与过去和解,努力爱自己。==希望春天来之前,我们一起面朝大海,春暖花开!==🤟 👏专栏:C++👏 👏专栏:Java语言👏👏专栏:Linux学习👏
👏专栏:C语言初阶👏👏专栏:数据结构👏👏专栏:备战蓝桥杯👏

@TOC


前言

今天我们继续学习算法,加油。这篇文章写的是最短距离总和问题。希望这篇可以有幸帮助到你,码字不易,请多多支持。 在这里插入图片描述

—————————————————————————————— 题目链接:AcWing 3512. 最短距离总和

问题描述

给定一个二维平面上的点集,找到一个点使得该点到点集中所有点的曼哈顿距离之和最小。

曼哈顿距离定义为两点在同一坐标系下横、纵坐标差的绝对值之和。

例如,(x1,y1)(x_1, y_1)(x2,y2)(x_2,y_2) 的曼哈顿距离为 x1x2+y1y2|x_1-x_2|+|y_1-y_2|

输入格式:

第一行包含整数 nn,表示点集中的点数。

接下来 nn 行每行包含两个整数 xix_iyiy_i,表示点集中的点的横、纵坐标。

输出格式:

输出点集中使得曼哈顿距离之和最小的点的曼哈顿距离之和。答案与标准答案误差不超过 10210^{-2} 即视为正确。

数据范围:

1n105,1\leqslant n\leqslant 10^5, 108xi,yi108-10^8\leqslant x_i, y_i\leqslant 10^8

思路分析

根据曼哈顿距离的定义,我们可以将其分解为横向距离和纵向距离的绝对值之和,即 x1x2+y1y2|x_1-x_2|+|y_1-y_2|

于是问题变为了,找到一个在横坐标上的中位数和在纵坐标上的中位数,作为答案点即可。可以使用 C++ STL 中的 nth_element 函数来求解中位数。具体步骤如下:

  1. 将所有点按 x 坐标排序,并取第 n/2 个点的 x 坐标值作为中位数 mxm_x
  2. 将所有点按 y 坐标排序,并取第 n/2 个点的 y 坐标值作为中位数 mym_y
  3. 构造点 (mx,my)(m_x, m_y),计算该点到所有点的曼哈顿距离之和即为所求。

时间复杂度:O(nlogn)O(n \log n)

C++代码实现

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5 + 10;
int n;
int a[N], b[N];

int main()
{
    cin >> n;
    for (int i = 0; i < n; ++i)
        cin >> a[i] >> b[i];

    nth_element(a, a + n / 2, a + n);
    nth_element(b, b + n / 2, b + n);

    long long res = 0;
    for (int i = 0; i < n; ++i)
        res += abs(a[i] - a[n / 2]) + abs(b[i] - b[n / 2]);

    cout << res << endl;

    return 0;
}

总结

该问题是一个典型的求解曼哈顿距离最小的点的问题,使用中位数可以较简单地求解。因此,这道题也可以作为类似问题的模板题目进行学习和练习。

最后

十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:

1.把时间尺度拉长,拉长十年看当下

2.不说负面情绪,只描述事实;

3.越专注于过好自己,能量和幸运越会照顾你; 只解决问题,不做没有意义的担心,输了就认;

4.学会原谅自己,要允许自己做错事,允许自己出现情绪波动,我知道你已经很努力很努力在做好了

5.所有你害怕的、想逃避的事情,最终都要面对,既然这样不如选择坦然面对。即使结果不如人愿,没关系,至少这个过程是享受的,而不是一路带着恐惧和害怕。

最后如果觉得我写的还不错,请不要忘记==点赞==✌,==收藏==✌,加==关注==✌哦(。・ω・。)

愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚==菜鸟==逐渐成为==大佬==。加油,为自己点赞!