这道华为秋招编程题考察的是典型的图论问题——如何在城市中合理布置信号塔,使得所有区域被覆盖的同时,信号塔之间的最小距离最大化。这在实际通信网络规划中非常常见,属于基础设施优化类问题。
题目给定一个二维矩阵表示的城区地图,其中1代表可建塔区域,0代表不可建区域。要求选择k个建塔点,使得这些塔之间的最小欧几里得距离尽可能大。换句话说,我们需要找到k个可行位置,使得它们两两之间的距离的最小值最大化。
这类问题在通信基站选址、无线传感器网络部署、物流中心规划等领域都有广泛应用。本质上是在资源有限(只能建k个塔)的情况下,优化设施的分布密度,避免资源浪费和信号干扰。
首先我们需要将这个问题抽象为可计算的模型:
这本质上是一个组合优化问题,解空间是C(|S|,k),即从|S|个点中选k个的所有组合。直接枚举所有组合显然不现实,需要更聪明的算法。
这个问题可以转化为一个二分查找+贪心验证的问题:
二分查找可能的距离d:
对于每个猜测的d,验证是否存在k个点,它们之间的距离都不小于d:
这种方法的复杂度是O(log(max_dist) * n^2),其中n是可建点的数量,对于题目给定的约束是完全可行的。
欧几里得距离的计算涉及开方,但我们可以优化:
java复制import java.util.ArrayList;
import java.util.List;
public class Solution {
public double maxMinDistance(int[][] grid, int k) {
// 收集所有可建塔的位置
List<int[]> points = new ArrayList<>();
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1) {
points.add(new int[]{i, j});
}
}
}
if (points.size() < k) return 0; // 可建点不足
// 二分查找可能的距离
double left = 0;
double right = getMaxDistance(points);
double result = 0;
while (right - left > 1e-6) { // 浮点数精度控制
double mid = left + (right - left) / 2;
if (canPlace(points, k, mid)) {
result = mid;
left = mid;
} else {
right = mid;
}
}
return result;
}
private double getMaxDistance(List<int[]> points) {
double max = 0;
for (int i = 0; i < points.size(); i++) {
for (int j = i + 1; j < points.size(); j++) {
max = Math.max(max, distance(points.get(i), points.get(j)));
}
}
return max;
}
private boolean canPlace(List<int[]> points, int k, double minDist) {
boolean[] visited = new boolean[points.size()];
int count = 0;
for (int i = 0; i < points.size(); i++) {
if (!visited[i]) {
count++;
if (count >= k) return true;
// 标记所有距离小于minDist的点为已访问
for (int j = i + 1; j < points.size(); j++) {
if (!visited[j] && distance(points.get(i), points.get(j)) < minDist) {
visited[j] = true;
}
}
}
}
return count >= k;
}
private double distance(int[] a, int[] b) {
return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));
}
}
cpp复制#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
class Solution {
public:
double maxMinDistance(vector<vector<int>>& grid, int k) {
vector<pair<int, int>> points;
for (int i = 0; i < grid.size(); ++i) {
for (int j = 0; j < grid[0].size(); ++j) {
if (grid[i][j] == 1) {
points.emplace_back(i, j);
}
}
}
if (points.size() < k) return 0.0;
double left = 0.0;
double right = getMaxDistance(points);
double result = 0.0;
while (right - left > 1e-6) {
double mid = left + (right - left) / 2;
if (canPlace(points, k, mid)) {
result = mid;
left = mid;
} else {
right = mid;
}
}
return result;
}
private:
double getMaxDistance(const vector<pair<int, int>>& points) {
double max_dist = 0.0;
for (int i = 0; i < points.size(); ++i) {
for (int j = i + 1; j < points.size(); ++j) {
max_dist = max(max_dist, distance(points[i], points[j]));
}
}
return max_dist;
}
bool canPlace(const vector<pair<int, int>>& points, int k, double min_dist) {
vector<bool> visited(points.size(), false);
int count = 0;
for (int i = 0; i < points.size(); ++i) {
if (!visited[i]) {
++count;
if (count >= k) return true;
for (int j = i + 1; j < points.size(); ++j) {
if (!visited[j] && distance(points[i], points[j]) < min_dist) {
visited[j] = true;
}
}
}
}
return count >= k;
}
double distance(const pair<int, int>& a, const pair<int, int>& b) {
return sqrt(pow(a.first - b.first, 2) + pow(a.second - b.second, 2));
}
};
python复制import math
def max_min_distance(grid, k):
# 收集所有可建塔的位置
points = []
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == 1:
points.append((i, j))
if len(points) < k:
return 0.0
# 二分查找
left, right = 0.0, get_max_distance(points)
result = 0.0
while right - left > 1e-6:
mid = left + (right - left) / 2
if can_place(points, k, mid):
result = mid
left = mid
else:
right = mid
return result
def get_max_distance(points):
max_dist = 0.0
for i in range(len(points)):
for j in range(i + 1, len(points)):
max_dist = max(max_dist, distance(points[i], points[j]))
return max_dist
def can_place(points, k, min_dist):
visited = [False] * len(points)
count = 0
for i in range(len(points)):
if not visited[i]:
count += 1
if count >= k:
return True
for j in range(i + 1, len(points)):
if not visited[j] and distance(points[i], points[j]) < min_dist:
visited[j] = True
return count >= k
def distance(a, b):
return math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2)
总复杂度:O(mn + p^2 + p^2 * log(max_dist/precision))。在实际应用中,p通常远小于mn,所以主要取决于矩阵大小和可建点数量。
总空间复杂度:O(p),非常高效。
在实际实现中,我们可以通过以下方式优化距离计算:
由于使用浮点数,比较时要注意:
如果遇到性能问题:
好的测试用例应该覆盖以下场景:
常规情况:
code复制grid = [
[1, 0, 1],
[0, 1, 0],
[1, 0, 1]
]
k = 2
边界情况:
大规模测试:
特殊形状:
在实际通信网络规划中,这个问题可以扩展为:
这些扩展会使问题更复杂,但基本思路仍然适用:将实际问题抽象为数学模型,设计高效算法求解,最后验证和调整。