这道题是让所有数组元素变成同一个值时的最小总开销,每个元素的变换成本是 |nums[i] - target| * cost[i]。

核心思路

这是一个带权中位数问题。

· 最小化 ∑ cost[i] * |nums[i] - target|
· 最优 target 是权值累积到中位数位置对应的 nums[i]

推导

假设把 nums 排序,每个 (nums[i], cost[i]) 看作一个“质量”为 cost[i] 的点。
目标:找某个位置 target 使加权绝对距离最小。

结论:总 cost 的最小值在 target 取加权中位数时达到。

加权中位数:

1. 按 nums 排序
2. 累计 cost,直到累计值 ≥ 总成本的一半
3. 这个位置的 nums 就是 target

为什么不是平均值?

绝对值函数的导数不连续,加权中位数是使加权绝对偏差最小的点,和平均数(使平方和最小)不同。

算法步骤

1. 将 (nums[i], cost[i]) 按 nums 排序
2. 计算总成本 totalCost = sum(cost)
3. 从头累加 cost,找到第一个累计 ≥ (totalCost + 1)/2 的位置,其 nums 即为 target
4. 计算 ∑ cost[i] * |nums[i] - target|

代码实现

```java
public long minCost(int[] nums, int[] cost) {
    int n = nums.length;
    Integer[] idx = new Integer[n];
    for (int i = 0; i < n; i++) idx[i] = i;
    // 按 nums 排序
    Arrays.sort(idx, (a, b) -> Integer.compare(nums[a], nums[b]));

    long totalCost = 0;
    for (int c : cost) totalCost += c;

    long half = (totalCost + 1) / 2;
    long accum = 0;
    int target = 0;

    // 找加权中位数
    for (int i = 0; i < n; i++) {
        int id = idx[i];
        accum += cost[id];
        if (accum >= half) {
            target = nums[id];
            break;
        }
    }

    // 计算最小开销
    long ans = 0;
    for (int i = 0; i < n; i++) {
        ans += (long) Math.abs(nums[i] - target) * cost[i];
    }
    return ans;
}
```

时间复杂度

O(n log n),排序占主导。

思考为什么不是二分?

二分需要对 target 的导数进行分析,这里目标函数是凸函数,用三分查找也行,但加权中位数直接 O(n log n) 更简洁。

如果你想要三分查找或前缀和优化(避免二次遍历)的版本,我也可以帮你写。

 

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐