题目:
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;
}
};