2163. 删除元素后和的最小差值 堆解法解析

2163. 删除元素后和的最小差值

给你一个下标从 0 开始的整数数组 nums ,它包含 3 * n 个元素。

你可以从 nums 中删除 恰好 n 个元素,剩下的 2 * n 个元素将会被分成两个相同大小的部分。

前面 n 个元素属于第一部分,它们的和记为 sumfirst
后面 n 个元素属于第二部分,它们的和记为 sumsecond
两部分和的 差值 记为 sumfirst - sumsecond

  • 比方说,sumfirst = 3 且 sumsecond = 2 ,它们的差值为 1 。

  • 再比方,sumfirst = 2 且 sumsecond = 3 ,它们的差值为 -1 。

请你返回删除 n 个元素之后,剩下两部分和的 差值的最小值 是多少。


首先我们通过删除极端位置获得first段与second段的取值范围:

在这里插入图片描述

step1:

由此我们获得了

  • 前段取值: [ 0 , 2 n − 1 ] [0, 2n-1] [0,2n1]

  • 后段取值: [ n , 3 n − 1 ] [n, 3n-1] [n,3n1]

这意味着即使无规则的、随意的删除n个元素,前段后段的取值都不会超过上述范围。


step2:

获得了这个范围就可以进行枚举,计算最小差值。

在计算最小差值之前,我们需要明确最小差值的定义: d i f f = f i r s t − s e c o n d diff = first-second diff=firstsecond

根据最小差值的定义我们不难得出我们程序设计的目的:

  • 前段数据 sumfirst 要尽可能小
  • 后段数据 sumsecond 要尽可能大

为了实现这一目的,

对于前半段范围的数据进行大根堆存储(降序排列),

而对于后半段范围的数据进行小根堆存储(升序排列)


step3:

本题仍存在一个难点:前段取值与后端取值具有重合部分

对于这个难点我们将创建一个数组对 [ n , 2 n ] [n,2n] [n,2n]这部分数据进行维护,并且动态更新答案

具体我们将在代码中进行说明


class Solution {
public:
    long long minimumDifference(vector<int>& nums) {
        int n = nums.size()/3;
        int i;
        long long ans = INT_MAX;
        priority_queue<int> maxq;  // 前半部分尽可能删除的大,构建一个大根堆(less<T>)
        priority_queue<int, vector<int>, greater<int>> minq;
        // 后半部分尽可能删除的小,构建一个小根堆(great<T>)
        long long first[200010];  // 在原数组中前i个元素选取最合理的n个元素的和
        // 构建first数组的将为最后计算ans提供极大的帮助
        
**********************************************************************
        // 计算前半部分
        first[n-1] = 0;  // 初始化前n-1个元素的和
        // 如果出现了极端情况,删除的num为最后n个数据,则first的取值有唯一性,即为前n个数
        // 换言之,后半部分无法取到这个范围内
        // 没有调整的空间,这里的first[n-1]的值即模拟这种情况
        for(i = 0; i < n; ++i) {
            first[n-1] += nums[i];
            maxq.push(nums[i]);
        } // 前n个值照单全收(必须!)
        for(i = n; i < 2*n; ++i) {   // 现在开始计算前后段交界面
            first[i] = first[i-1] + nums[i];  
            maxq.push(nums[i]);
            int maxv = maxq.top();
            maxq.pop();
            first[i] -= maxv;
        }
        // 通过这里的代码我们明显看出来n~2n-1部分sum first的值可以进行调整
        // 调整的大原则就是前半部分尽可能小,因此删除的数尽可能大
***************************************************************************
 
    	// 计算后半部分
        long long sec = 0;
        for(i = 3*n-1; i >= 2*n; --i) {
            sec += nums[i];
            minq.push(nums[i]);
        }  // 前段数据不可能达到这个长度,因此这部分数据照单全收
****************************************************************************
    
    	// 动态计算最大值:
        ans = first[2*n-1]-sec;   
        // 这里相当于后段数据范围取到[2n~3n-1],前段数据取到[0,2n-1]   ---  这只是起点
        // 开始从起点一直推到终点  ---- 终点就是n 因为n后端数据无法取到 [0~n-1]
        // 这里体现出了first数组的作用
        for(i = 2*n-1;i>=n;--i) {
            sec += nums[i];
            minq.push(nums[i]);
            int minv = minq.top();
            minq.pop();
            sec-=minv;
            ans = min(first[i-1]- sec, ans);   // 动态更新最小值
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值