LeetCode每日一题(2448. Minimum Cost to Make Array Equal)

给定两个数组nums和cost,你需要通过增加或减少数组nums中任意元素的值使其所有元素相等,每次操作的成本为cost[i]。返回使所有元素相等的最小总成本。例如,对于nums = [1,3,5,2]和cost = [2,3,1,14],最小成本为8。文章介绍了如何通过计算前缀和和后缀和找到最优解。" 82892058,7413980,Vue实现导航条滚动效果,"['Vue', 'JavaScript', 'CSS', '动画效果', '前端开发']

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

You are given two 0-indexed arrays nums and cost consisting each of n positive integers.

You can do the following operation any number of times:

Increase or decrease any element of the array nums by 1.
The cost of doing one operation on the ith element is cost[i].

Return the minimum total cost such that all the elements of the array nums become equal.

Example 1:

Input: nums = [1,3,5,2], cost = [2,3,1,14]
Output: 8

Explanation: We can make all the elements equal to 2 in the following way:

  • Increase the 0th element one time. The cost is 2.
  • Decrease the 1st element one time. The cost is 3.
  • Decrease the 2nd element three times. The cost is 1 + 1 + 1 = 3.
    The total cost is 2 + 3 + 3 = 8.
    It can be shown that we cannot make the array equal with a smaller cost.

Example 2:

Input: nums = [2,2,2,2,2], cost = [4,2,8,1,3]
Output: 0

Explanation: All the elements are already equal, so no operations are needed.

Constraints:

  • n == nums.length == cost.length
  • 1 <= n <= 105
  • 1 <= nums[i], cost[i] <= 106

首先明确一点, 最终 nums 中的那个值一定是初始 nums 中的某一个值, 也就是整个过程一定是所有 nums 中的数字往 nums 中的某一个数字靠拢的过程。至于为啥, 不知道, 直觉。

以 nums = [1,3,5,2], cost = [2,3,1,14]为例

我们先将 nums 和 cost 进行排序, 注意要关联排序, 不要破坏 nums 与 cost 中的对应关系。
得到
nums = [1, 2, 3, 5]
cost = [2, 14, 3, 1]
(我实现的时候是用的倒序排序, 现在回过头来看, 其实正序倒序应该都可以, 这里用正序)

假设
total_cost = 0
开始遍历
nums[0] = 1
nums[1] = 2
此时我们要么把 1 变成 2, 要么把 2 变成 1, 此时我们先考虑 1 变成 2 的情况, 2 变 1 的我们后面单独考虑。因为 cost[0] = 2 所以 1 变 2 的成本就是 2
total_cost = 2
再往下走
nums[1] = 2
nums[2] = 3
此时我们还是只考虑 2 变 3 的情况, cost[1] = 14, 所以 2 变成 3 的成本就是 14, 但是前面还有个 1, 此时 1 已经变成 2 了,也是从 2 变成 3, 但是成本不一样, cost[0] = 2, 所以都变成 3 的成本就是 2 + 14 = 16, 加上前面 1 变成 2 的成本, total_cost = 18
看出点端倪来了吗?假设 dp[i]是 nums[…i]都变成 nums[i]的成本, 那 dp[i] = dp[i-1] + (nums[i] - nums[i-1]) * sum(cost[0], cost[1], …cost[i-1]), 用人话说就是要都变成 nums[i]就要在都变成 nums[i-1]的基础上在加上两者的差值与 cost prefix sum 的乘积
这样算出来的 dp[i]就是左侧都变成 nums[i]成本, 那剩下的右侧的 nums[i+1…]呢?很简单,还是用上面的公式, 反过来遍历 nums 就可以了, 只不过 cost prefix sum 变成了 suffix sum。

这样我们就得到了所有左侧数字变成 nums[i]的成本 left_cost 和右侧数字都变成 nums[i]的成本 right_cost, 我们只需要找出 left_cost[i] + right_cost[i]的最小值就可以了

因为代码实现中途改过几遍, 不是很清晰, 比较混乱, 仅供参考



impl Solution {
    pub fn min_cost(nums: Vec<i32>, cost: Vec<i32>) -> i64 {
        let mut list: Vec<(i64, i64)> = nums.into_iter().zip(cost).map(|(n, c)| (n as i64, c as i64)).collect();
        list.sort_by_key(|n| (-n.0, -n.1));
        let cost_prefix: Vec<i64> = list
            .iter()
            .scan(0, |s, &(_, cost)| {
                *s += cost;
                Some(*s)
            })
            .collect();
        let total_cost_prefix: Vec<i64> = list
            .windows(2)
            .enumerate()
            .scan(0, |s, (i, w)| {
                let (num0, _) = w[0];
                let (num1, _) = w[1];
                *s += cost_prefix[i] * (num0 - num1);
                Some(*s)
            })
            .collect();
        let mut cost_suffix: Vec<i64> = list
            .iter()
            .rev()
            .scan(0, |s, &(_, cost)| {
                *s += cost;
                Some(*s)
            })
            .collect();
        cost_suffix.reverse();
        let mut total_cost_suffix: Vec<i64> = list
            .windows(2)
            .enumerate()
            .rev()
            .scan(0, |s, (i, w)| {
                let (num0, _) = w[0];
                let (num1, _) = w[1];
                *s += (num0 - num1) * cost_suffix[i + 1];
                Some(*s)
            })
            .collect();
        total_cost_suffix.reverse();
        total_cost_suffix.push(0);
        let mut ans = total_cost_suffix[0];
        for i in 0..total_cost_prefix.len() {
            ans = ans.min(total_cost_prefix[i] + total_cost_suffix[i + 1]);
        }
        ans
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值