力扣:2448. 使数组相等的最小开销

107 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第29天,点击查看活动详情

力扣——2448. 使数组相等的最小开销

2448. 使数组相等的最小开销 - 力扣(LeetCode)

给你两个下标从 0 开始的数组 nums 和 cost ,分别包含 n 个 正 整数。

你可以执行下面操作 任意 次:

将 nums 中 任意 元素增加或者减小 1 。 对第 i 个元素执行一次操作的开销是 cost[i] 。

请你返回使 nums 中所有元素 相等 的 最少 总开销。

示例 1:
输入:nums = [1,3,5,2], cost = [2,3,1,14]
输出:8
解释:我们可以执行以下操作使所有元素变为 2 :
​
- 增加第 0 个元素 1 次,开销为 2 。
- 减小第 1 个元素 1 次,开销为 3 。
- 减小第 2 个元素 3 次,开销为 1 + 1 + 1 = 3 。
  总开销为 2 + 3 + 3 = 8 。
  这是最小开销。

提示:

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

问题解析

  1. 设置前缀数组f,f[i]表示:把0~i-1的元素都变成i元素的总开销为f[i]。
  2. 设置后缀数组f2,f2[i]表示:把i+1~n-1的元素都变成i元素的总开销为f2[i]。
  3. 为了方便运算,我们将nums[i]和cost[i]绑定后升序排序。
  4. 可以知道f[0]=0,f2[n-1]=0。剩下的该如何递推呢?
  5. 我们用前缀树组f来想,当我们遍历到第i个位置时,f[i-1]是把前面所有元素都变成了nums[i-1]的花费,那么我只用把这些元素都变成nums[i]即可,这一部分的花费就为:(nums[i]-nums[i-1])*(前面所有元素的cost之和)。
  6. 可知f的递推公式为:f[i]=f[i-1]+(nums[i]-nums[i-1])*(前面所有元素的cost之和)。
  7. 同理可得f2的递推公式:f[i]=f[i+1]+(nums[i+1]-nums[i])*(后面所有元素的cost之和)。
  8. 然后我们枚举i,维护f[i]+f2[i]的最小值即可。

AC代码

class Solution {
public:
    typedef long long ll;
    typedef pair<ll,ll>PII;
    ll f[100050],f2[100050];
    long long minCost(vector<int>& nums, vector<int>& cost) {
        ll n=nums.size();
        vector<PII>v(n);
        for(int i=0;i<n;i++)
        {
            v[i].first=nums[i];
            v[i].second=cost[i];
        }
        sort(v.begin(),v.end());
        ll ans=v[0].second;
        for(int i=1;i<n;i++)
        {
            f[i]=f[i-1]+ans*(v[i].first-v[i-1].first);
            ans+=v[i].second;
        }
        ans=v[n-1].second;
        for(int i=n-2;i>=0;i--)
        {
            f2[i]=f2[i+1]+ans*((v[i+1].first-v[i].first));
            ans+=v[i].second;
        }
        ll mn=1e18;
        for(int i=0;i<n;i++)
        {
            mn=min(mn,f[i]+f2[i]);
        }
        return mn;
    }
};