刚开始做的时候想法就是先把最大和求出来,然后再删掉一些数。
求最大的只要遍历一遍,正数都加起来就可以了。
接下来要删掉一些数,为了让正数和负数统一,我把所有正数都翻转成了负数,这样当删去一个数时就可以直接加而不需要分正数和负数判断。
再接下来就不知道要怎么做了,然后看了题解。
要找出前k大的和,那么对于目前已经求出正数和,并且所有正数都翻转成了负数的情况而言,就需要在绝对值比较小的数(由于都是非正数,所以也就是较大的数)中找一些数作为要加上的数。
用优先队列存sum和下一个要处理的位置,按sum降序排列。当处理到第i个数时,为了让所有的情况都被考虑到,要向优先队列存入加上i-1的和没有加上i-1的值。
例如有三个数,用二进制位来分别表示它们,则:
可以看到所有八种情况都被考虑到了。
由于我们只需要从优先队列里取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;
}
};
感觉这个是取子序列的又一个重要方法。