2024.3.9每日一题

LeetCode

找出数组的第 K 大和

题目链接:2386. 找出数组的第 K 大和 - 力扣(LeetCode)

题目描述

给你一个整数数组 nums 和一个 整数 k 。你可以选择数组的任一 子序列 并且对其全部元素求和。

数组的 第 k 大和 定义为:可以获得的第 k最大 子序列和(子序列和允许出现重复)

返回数组的 第 k 大和

子序列是一个可以由其他数组删除某些或不删除元素排生而来的数组,且派生过程不改变剩余元素的顺序。

**注意:**空子序列的和视作 0

示例 1:

复制代码
输入:nums = [2,4,-2], k = 5
输出:2
解释:所有可能获得的子序列和列出如下,按递减顺序排列:
- 6、4、4、2、2、0、0、-2
数组的第 5 大和是 2 。

示例 2:

复制代码
输入:nums = [1,-2,3,4,-10,12], k = 16
输出:10
解释:数组的第 16 大和是 10 。

提示:

  • n == nums.length
  • 1 <= n <= 105
  • -109 <= nums[i] <= 109
  • 1 <= k <= min(2000, 2n)

思路

困难题 cv大法

灵神题解

代码

C++
C++ 复制代码
class Solution {
public:
    long long kSum(vector<int> &nums, int k) {
        long sum = 0;
        for (int &x : nums) {
            if (x >= 0) {
                sum += x;
            } else {
                x = -x;
            }
        }
        ranges::sort(nums);

        auto check = [&](long sum_limit) -> bool {
            int cnt = 1; 
            function<void(int, long long)> dfs = [&](int i, long long s) {
                if (cnt == k || i == nums.size() || s + nums[i] > sum_limit) {
                    return;
                }
                cnt++; // s + nums[i] <= sum_limit
                dfs(i + 1, s + nums[i]);
                dfs(i + 1, s); 
            };
            dfs(0, 0);
            return cnt == k; 
        };

        long long left = -1, right = accumulate(nums.begin(), nums.end(), 0LL);
        while (left + 1 < right) { 
            long long mid = (left + right) / 2;
            (check(mid) ? right : left) = mid;
        }
        return sum - right;
    }
};
Java
java 复制代码
class Solution {
    public long kSum(int[] nums, int k) {
        long sum = 0, right = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] >= 0) {
                sum += nums[i];
            } else {
                nums[i] = -nums[i];
            }
            right += nums[i];
        }
        Arrays.sort(nums);

        long left = -1;
        while (left + 1 < right) { // 开区间二分,原理见【前置知识】
            long mid = (left + right) / 2;
            cnt = k - 1; // 空子序列算一个
            dfs(0, mid, nums);
            if (cnt == 0) { // 找到 k 个元素和不超过 mid 的子序列
                right = mid;
            } else {
                left = mid;
            }
        }
        return sum - right;
    }

    private int cnt;

    // 反向递归,增加改成减少,这样可以少传一些参数
    private void dfs(int i, long s, int[] nums) {
        if (cnt == 0 || i == nums.length || s < nums[i]) {
            return;
        }
        cnt--;
        dfs(i + 1, s - nums[i], nums); // 选
        dfs(i + 1, s, nums); // 不选
    }
}
相关推荐
Tisfy7 分钟前
LeetCode 837.新 21 点:动态规划+滑动窗口
数学·算法·leetcode·动态规划·dp·滑动窗口·概率
CoovallyAIHub39 分钟前
为高空安全上双保险!无人机AI护航,YOLOv5秒判安全带,守护施工生命线
深度学习·算法·计算机视觉
huangzixuan10071 小时前
08.18总结
算法·深度优先·图论
逆向菜鸟1 小时前
【摧毁比特币】椭圆曲线象限细分求k-陈墨仙
python·算法
DolphinDB1 小时前
DolphinDB 回测插件快速上手
算法
利刃大大1 小时前
【动态规划:路径问题】最小路径和 && 地下城游戏
算法·动态规划·cpp·路径问题
武大打工仔2 小时前
用 Java 复现哲学家就餐问题
算法
要做朋鱼燕2 小时前
【数据结构】用堆解决TOPK问题
数据结构·算法
秋难降3 小时前
LRU缓存算法(最近最少使用算法)——工业界缓存淘汰策略的 “默认选择”
数据结构·python·算法
tkevinjd3 小时前
图论\dp 两题
leetcode·动态规划·图论