找出数组的第k大和

给你一个整数数组 nums 和一个正整数 k 。你可以选择数组的任一子序列并且对其全部元素求和。
数组的第 k 大和 定义为:可以获得的第 k 个 最大 子序列和(子序列和允许出现重复)
返回数组的第 k 大和

1. 转化问题 + 大根堆

class Solution {
public:
    long long kSum(vector<int> &nums, int k) {
        long sum = 0L;
        for (int &x : nums) //对正数求和,同时将负数变为正数
            if (x >= 0) sum += x;
            else x = -x;
        sort(nums.begin(), nums.end()); //排序
        //原问题转化成为从原数组中选择元素减去
        priority_queue<pair<long, int>> pq; //大根堆,存储扩张时的状态,类似深度优先搜索
        pq.emplace(sum, 0); //第一大
        while (--k) { //循环运行k-1次
            auto[sum, i] = pq.top(); //当前最大值和当前待操作下标
            pq.pop();
            if (i < nums.size()) {
                //扩张时两种选择,第一种减去当前操作数,第二种跳过当前操作数
                //但是由于涉及到大根堆优先级,所以不能直接跳过,而是后面补回来
                pq.emplace(sum - nums[i], i + 1); // 保留 nums[i-1]
                if (i) pq.emplace(sum - nums[i] + nums[i - 1], i + 1); // 不保留 nums[i-1],把之前减去的加回来
            }
        }
        return pq.top().first;
    }
};

2. 二分

class Solution {
public:
    long long kSum(vector<int> &nums, int k) {
        //预处理
        long sum = 0L;
        for (int &x : nums) {
            if (x >= 0) sum += x;
            else x = -x;
        }
        sort(nums.begin(), nums.end());
        
        --k;//
        auto check = [&](long limit) -> bool {
            int cnt = 0;
            //暴搜所有小于等于mid的序列和个数
            function<void(int, long)> f = [&](int i, long s) {
                if (i == nums.size() || cnt >= k || s + nums[i] > limit) return; //边界条件
                ++cnt; //因为后面都可以不选,满足条件序列个数加一
                f(i + 1, s + nums[i]); // 选
                f(i + 1, s); // 不选
            };
            f(0, 0L);
            return cnt >= k;
        };

        //求最大的结果,从数组中减去最小的序列,即有k个序列和满足值即可
        long left = 0L, right = accumulate(nums.begin(), nums.end(), 0L);
        while (left < right) {
            long mid = (left + right) / 2; //累计和
            if (check(mid)) right = mid; //小于等于累计和的序列个数大于等于阈值
            else left = mid + 1; //小于阈值,不满足条件
        }
        return sum - left; //减去对应累计和
    }
};

posted @ 2023-05-30 00:53  失控D大白兔  阅读(42)  评论(0编辑  收藏  举报