[Leetcode] 296. Best Meeting Point 解题报告

本文详细介绍了LeetCode 296题目的解法,讨论了初始的暴力BFS解法及其高时间复杂度和空间复杂度问题。接着提出了一种更巧妙的解决方案,通过找到所有人在x和y轴上的中位数作为会合点,以达到总旅行距离最小。这种方法的时间复杂度为O(mn),空间复杂度为O(k),其中m和n是网格的行和列数,k是人数。并给出了具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

A group of two or more people wants to meet and minimize the total travel distance. You are given a 2D grid of values 0 or 1, where each 1 marks the home of someone in the group. The distance is calculated using Manhattan Distance, where distance(p1, p2) = |p2.x - p1.x| + |p2.y - p1.y|.

For example, given three people living at (0,0)(0,4), and (2,2):

1 - 0 - 0 - 0 - 1
|   |   |   |   |
0 - 0 - 0 - 0 - 0
|   |   |   |   |
0 - 0 - 1 - 0 - 0

The point (0,2) is an ideal meeting point, as the total travel distance of 2+2+2=6 is minimal. So return 6.

思路

我开始想到的思路是暴力BFS,也就是对于每个人的home,采用BFS生成一张距离图,然后将所有生成的距离图的对应位置加起来,并找出距离之和最小的那个位置即为所求。显然这种思路是可行的。假设grid的行和列个数分别是m和n,一共有k个人,那么算法的时间复杂度高达O(kmn),空间复杂度也高达O(kmn)。虽然空间复杂度还可以优化,但是显然不是一个好的算法。

仔细观察这道题目,其实有更巧妙的算法,这是因为grid中的格与格之间都是互通的,中间没有障碍物。我在网上看到一个比BFS更巧妙的算法是这样的(盗图一张):先在一维上思考这个问题:相当于在一条直线上有A, B, C, D, E五个点,我们要在这个直线上找到一个点P,使得所有点到P点的距离之和最小。


我们可以看到A和E两点距离之和的最小点肯定出现在[A, E]闭区间内(实际上该区间内任意一点都满足距离之和最小的条件),而到B和D距离之和最小的点肯定出现在[B, D],到C点的最短距离就只能在C上了。这是奇数个点的情况,偶数个点可以在最中间的两个点之间的任意位置。因此我们要找的点其实就是所有点的中位数(注意不是平均数)。所以我们只需要将这几个点的x,y坐标分别记录下来,然后进行排序就可以。

因为x和y方向上是互无干扰的,所以其各自的中位数就是我们所要找的最佳见面位置,具体代码如下。该算法的空间复杂度是O(k),其中k是人数;时间复杂度是O(mn)。注意nth_element函数的均摊时间复杂度是O(k)。

代码

class Solution {
public:
    int minTotalDistance(vector<vector<int>>& grid) {
        vector<int> X, Y;
        for (int i = 0; i < grid.size(); ++i) {
            for (int j = 0; j < grid[i].size(); ++j) {
                if (grid[i][j] == 1) {
                    X.push_back(i);
                    Y.push_back(j);
                }
            } 
        }
        nth_element(X.begin(), X.begin() + X.size() / 2, X.end());
        nth_element(Y.begin(), Y.begin() + Y.size() / 2, Y.end());
        int ret = 0, x_med = X[X.size() / 2], y_med = Y[Y.size() / 2];
        for (int i = 0; i < X.size(); ++i) {
            ret += abs(X[i] - x_med) + abs(Y[i] - y_med);
        }
        return ret;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值