LC2386. 找出数组的第 K 大和

在这里插入图片描述

刚开始做的时候想法就是先把最大和求出来,然后再删掉一些数。

求最大的只要遍历一遍,正数都加起来就可以了。

接下来要删掉一些数,为了让正数和负数统一,我把所有正数都翻转成了负数,这样当删去一个数时就可以直接加而不需要分正数和负数判断。

再接下来就不知道要怎么做了,然后看了题解。

要找出前k大的和,那么对于目前已经求出正数和,并且所有正数都翻转成了负数的情况而言,就需要在绝对值比较小的数(由于都是非正数,所以也就是较大的数)中找一些数作为要加上的数。

用优先队列存sum和下一个要处理的位置,按sum降序排列。当处理到第i个数时,为了让所有的情况都被考虑到,要向优先队列存入加上i-1的和没有加上i-1的值。

例如有三个数,用二进制位来分别表示它们,则:

100
101
110
111
000
001
011
010

可以看到所有八种情况都被考虑到了。

由于我们只需要从优先队列里取k个数,而每次最多也只推入两个数,所以它贡献的复杂度也只是 O ( k l o g k ) O(klogk) O(klogk)

class Solution {
    
    
public:
#define plli pair<long long,int>
    long long kSum(vector<int>& nums, int k) {
    
    
        long long sum=0;
        int sz=nums.size();
        for(int i=0;i<sz;++i)
        {
    
    
            if(nums[i]>0)
            {
    
    
                sum+=nums[i];
                nums[i]=-nums[i];
            }
        }
        sort(nums.begin(),nums.end(),[](int a,int b){
    
    return a>b;});
        priority_queue<plli> q;
        q.push(plli(sum,0));
        while(--k)
        {
    
    
            plli p=q.top();
            q.pop();
            if(p.second<sz)
            {
    
    
                q.push(plli(p.first+nums[p.second],p.second+1));
                if(p.second)
                    q.push(plli(p.first+nums[p.second]-nums[p.second-1],p.second+1));
            }
        }
        return q.top().first;
    }
};

感觉这个是取子序列的又一个重要方法。

猜你喜欢

转载自blog.csdn.net/m0_49792815/article/details/129492567