1. 题目背景与核心需求解析

这道题目来自华为2025年秋招非AI方向的机试题库,属于典型的算法优化类问题。题目要求在城市中布置信号塔时,找到所有相邻信号塔之间的最小距离的最大可能值。这实际上是一个在约束条件下求最优解的问题,在通信基站部署、物流仓储规划等领域都有实际应用价值。

1.1 问题形式化描述

给定一个有序数组表示城市道路的位置(假设道路是直线型的),需要在这些位置上布置一定数量的信号塔。要求所有相邻信号塔之间的距离都不小于某个值d,我们的目标是找到这个d的最大可能值。

输入格式:

  • 道路位置数组(已排序):positions = [x1, x2, ..., xn]
  • 需要布置的信号塔数量:k

输出:

  • 相邻信号塔之间的最小距离的最大可能值

1.2 实际应用场景

这个问题在实际中有多个应用场景:

  1. 通信基站部署:确保基站覆盖范围不重叠的同时最大化覆盖区域
  2. 物流仓库选址:在一条运输线上合理分布仓库,降低运输成本
  3. 城市设施规划:如公交站、消防站等公共设施的合理布局

2. 解题思路与算法选择

2.1 暴力搜索法的局限性

最直观的想法是尝试所有可能的信号塔布置组合,然后找出其中满足条件的最小距离的最大值。但是这种方法的时间复杂度是组合数C(n,k),当n和k较大时(比如n=10000,k=5000),计算量会变得不可接受。

2.2 二分查找与贪心算法的结合

更高效的解法是结合二分查找和贪心算法:

  1. 首先确定搜索范围:最小距离d的可能取值在0到positions[-1]-positions[0]之间
  2. 使用二分查找在这个范围内搜索可能的d值
  3. 对于每个候选的d值,使用贪心算法验证是否可以布置k个信号塔

2.3 算法正确性证明

这种方法的正确性基于以下观察:

  1. 如果某个d值可行,那么所有小于d的值也都可行
  2. 如果某个d值不可行,那么所有大于d的值也都不可行
  3. 这满足二分查找的应用条件,可以高效地找到边界值

3. 代码实现与解析

3.1 Java实现

import java.util.Arrays;

public class Solution {
    public int maxMinDistance(int[] positions, int k) {
        Arrays.sort(positions);
        int left = 0;
        int right = positions[positions.length - 1] - positions[0];
        int result = 0;
        
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (canPlace(positions, k, mid)) {
                result = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        
        return result;
    }
    
    private boolean canPlace(int[] positions, int k, int d) {
        int count = 1;
        int last = positions[0];
        
        for (int i = 1; i < positions.length; i++) {
            if (positions[i] - last >= d) {
                count++;
                last = positions[i];
                if (count >= k) return true;
            }
        }
        
        return count >= k;
    }
}

3.2 C++实现

#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    int maxMinDistance(vector<int>& positions, int k) {
        sort(positions.begin(), positions.end());
        int left = 0;
        int right = positions.back() - positions.front();
        int result = 0;
        
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (canPlace(positions, k, mid)) {
                result = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        
        return result;
    }
    
private:
    bool canPlace(vector<int>& positions, int k, int d) {
        int count = 1;
        int last = positions[0];
        
        for (int i = 1; i < positions.size(); i++) {
            if (positions[i] - last >= d) {
                count++;
                last = positions[i];
                if (count >= k) return true;
            }
        }
        
        return count >= k;
    }
};

3.3 Python实现

def max_min_distance(positions, k):
    positions.sort()
    left, right = 0, positions[-1] - positions[0]
    result = 0
    
    while left <= right:
        mid = (left + right) // 2
        if can_place(positions, k, mid):
            result = mid
            left = mid + 1
        else:
            right = mid - 1
    
    return result

def can_place(positions, k, d):
    count = 1
    last = positions[0]
    
    for i in range(1, len(positions)):
        if positions[i] - last >= d:
            count += 1
            last = positions[i]
            if count >= k:
                return True
    
    return count >= k

4. 算法复杂度分析

4.1 时间复杂度

  1. 排序阶段:O(n log n),其中n是道路位置的数量
  2. 二分查找阶段:O(log(max_dist)),其中max_dist是最大可能距离
  3. 每次验证:O(n) 总体时间复杂度为O(n log n + n log(max_dist)),对于大多数实际应用场景,这已经足够高效

4.2 空间复杂度

除了输入数据外,算法只需要常数级别的额外空间,因此空间复杂度是O(1)

5. 边界条件与测试用例

5.1 典型测试用例

  1. 基础用例:

    • 输入:[1,2,3,4,5], k=3
    • 输出:2(布置在1,3,5位置)
  2. 所有位置都相同:

    • 输入:[5,5,5,5], k=2
    • 输出:0(只能布置在相同位置)
  3. 最大距离用例:

    • 输入:[1,10,100,1000], k=2
    • 输出:999(布置在1和1000位置)

5.2 特殊边界情况

  1. k=1:可以布置在任何位置,返回任意大值(实际应返回无穷大,但根据题目约束可能返回特定值)
  2. k=n:必须在每个位置都布置信号塔,返回最小相邻距离
  3. 空数组或k=0:根据题目要求处理异常情况

6. 算法优化与变种

6.1 提前终止优化

在canPlace函数中,一旦count达到k就可以提前返回true,不需要继续遍历剩余位置。这个小优化可以在某些情况下显著减少实际运行时间。

6.2 变种问题

  1. 多维空间布置:将问题扩展到二维或三维空间
  2. 带权重布置:不同位置布置信号塔的成本不同
  3. 动态布置:道路位置会随时间变化

7. 实际工程应用建议

7.1 性能优化

对于大规模数据(n>1e6),可以考虑以下优化:

  1. 使用更高效的排序算法(如基数排序,如果数据范围有限)
  2. 并行化验证过程
  3. 使用近似算法快速得到初步解

7.2 工程实现注意事项

  1. 输入验证:确保positions数组已排序,k值合法
  2. 数值溢出:对于大整数情况,使用long类型
  3. 浮点数处理:如果位置坐标是浮点数,需要调整比较方式

8. 常见错误与调试技巧

8.1 常见错误

  1. 忘记排序输入数组
  2. 二分查找边界条件处理不当
  3. 贪心验证时计数逻辑错误
  4. 整数溢出问题

8.2 调试建议

  1. 打印中间结果:在二分查找过程中打印left, right, mid值
  2. 小规模测试:先用小数据验证算法正确性
  3. 边界测试:专门测试k=1, k=n等边界情况

9. 华为机试准备建议

9.1 算法准备重点

  1. 掌握基础算法:排序、二分查找、贪心算法
  2. 熟悉常见算法模板:如本题的二分答案模板
  3. 练习代码实现速度:华为机试有时间限制

9.2 解题技巧

  1. 先理解题意,明确输入输出
  2. 思考暴力解法,再考虑优化
  3. 注意边界条件和特殊输入
  4. 编写清晰的代码,适当添加注释

10. 扩展学习资源

  1. 《算法导论》中的二分查找和贪心算法章节
  2. LeetCode类似题目:
      1. Koko Eating Bananas
      1. Capacity To Ship Packages Within D Days
      1. Divide Chocolate
  3. 华为OJ其他题目练习

在实际工程应用中,这类问题经常出现在资源分配、设施布局等场景。掌握这种二分答案的思路,可以解决一大类"最大化最小值"或"最小化最大值"的问题。建议读者在理解本题的基础上,尝试解决上面提到的LeetCode类似题目,加深对这种解题模式的理解。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐