Leetcode2542-最大子序列的分数

1.问题转换

首先明确题意,要选取的值和num1,num2两个数组都有关,但是num1中选取的是k个数,num2中选取的是1个数,显然num2中的数所占的权重较大(对结果影响较大),所以我们就可以对num2进行排序(也可以对nums1进行排序,就是对nums1排列以后枚举时获取nums2最小值特麻烦,就不再赘述了,有兴趣的读者可以思考一下),枚举num2中的每个数,然后确定num1中对应的k个数,但是选取元素时 num1 和 num2 对应的索引要一样,所以不能对num2直接排序,那么就对num2所对应的索引进行排序即可,对num2的索引,按照num2的值从大到小进行排序,为什么从大到小,因为要过滤在num2中前k-1个数,在第k个数进行计算,看到下文便可知

java 复制代码
        int len = nums1.length;
        Integer[] ids = new Integer[len];
        for (int i = 0; i < len; i++) {
            ids[i] = i;
        }
        //按照nums2[]  数组元素降序后排列的下标
        Arrays.sort(ids, (i, j) -> nums2[j] - nums2[i]);

进行这样的排序之后,所得到的效果就是 nums2ids\[0] 就是nums2中最大的元素,nums2ids\[1]就是num2中第二大的元素...

要设计一个小顶堆,确保这k个数在遍历时,是遍历到的最大值,如果每次遇到一个值比堆顶元素大,那么就替换堆顶元素,并且定义一个变量 sum 记录堆中元素的总和,便于计算

2. 要理解的三个点

A. nums2ids\[i] i 从 0 -> len - 1 遍历 nums2ids\[i] 就是降序的

B. 要从nums2\[\] 中第k大的元素 x 开始遍历,如果选了前面的数(比x大的数),那么nums1\[\] 就凑不出k个数满足配件,例如图片中的例子,如果选了nums2中最大的数4,对应的下标只有一个3,就凑不出3个下标,因为4在nums2中就是最大的,不存在两个比4小的数

C. nums1\[\] nums2\[\] 中选取的下标都是一样的,nums2ids\[i] 选取的下标是 idsi 那么nums1\[\] 选取的下标也得是idsi, 所以先把 前k个 idsi 所以对应的nums1\[\] 的元素入小顶堆

3. 代码编写

首先就是将num2的最大值索引映射到ids上,这样 i 从 0 -> len - 1 遍历 nums2ids\[i] 就是降序的,因为必须从num2中的第k个元素开始计算(至于为什么,看第二点),所以就跳过前k个num2中最大的数(跳过的索引为ids0....k-1),对应的就把num1ids\[0.....k-1] 这些元素入堆,并且计算和,此时已经有第一个结果,就定义res存储这个结果。

因为通过k你已经确定了nums2的最大值了,因为位置是共同变换的,所以相应的nums1的和就是初始值,但是这个答案不一定是最大的,那么我们就需要往后选,num2往后选必然会越来越小,所以影响答案的是num1新加的数,不光要维护nums2最小值,还要维护nums1的和,每次都会新加一个数,小根堆维护的最小的k个元素,当加入的元素要比最小的小的话就更新

然后就是遍历剩下的nums2中的len-k个元素,也就是比nums2ids\[k-1] 小的元素,此时对应的小顶堆中维护的num1\[\] 中的值也应该发生变化,因为nums2\[\] 的索引发生了变化,如果nums1ids\[i] > minHeap.peek() 那么就弹出堆顶元素,将nums1ids\[i]入堆,确保堆中元素是遍历过的元素里面最大的k个元素,同时更新res和sum,具体代码如下

java 复制代码
    public static long maxScore(int[] nums1, int[] nums2, int k) {
        //需要以及难理解的3点:
        //1. nums2[ids[i]]   i 从 0 -> len - 1  遍历   nums2[ids[i]] 就是降序的
        //2. 要从nums2[] 中第k大的元素  x  开始遍历,如果选了前面的数(比x大的数),那么nums1[] 就凑不出k个数满足配件
        //3. nums1[]  nums2[]  中选取的下标都是一样的,nums2[ids[i]]  选取的下标是 ids[i]    那么nums1[] 选取的下标也得是ids[i]
        //   所以先把 前k个 ids[i] 所以对应的nums1[] 的元素入小顶堆
        int len = nums1.length;
        Integer[] ids = new Integer[len];
        for (int i = 0; i < len; i++) {
            ids[i] = i;
        }
        //想要对nums2[]  进行排序,但是对应的索引不能边,就对索引按照nums2的元素从大到小进行排序
        Arrays.sort(ids, (i, j) -> nums2[j] - nums2[i]);
        //从 0 -> len   遍历nums2[ids[i]]          就得到的是nums2从大到小遍历的结果
        //直接获取nums1中最大的前k个数即可
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        long sum = 0;
        for (int i = 0; i < k; i++) {
            sum += nums1[ids[i]];
            minHeap.add(nums1[ids[i]]);
        }
        // 枚举的nums2[] 中的最大值,一定不是整个数组的最大值,而是nums2中的第k大的值,
        // 这样的话,nums1中才能找到k个与之对应的元素,如果找nums2中最大值,那么对应的nums1中的值只有一个
        // 所以必须得从nums2的第k大个元素开始,枚举的num2一直变小,然后对应的minHeap中的值变大
        long res = sum * nums2[ids[k - 1]];
        for (int i = k; i < len; i++) {
            int x = nums1[ids[i]];
            if (x > minHeap.peek()) {
                sum += x - minHeap.poll();
                minHeap.add(x);
                res = Math.max(res, nums2[ids[i]] * sum);
            }
        }
        return res;
    }

4.总结

说实话,这道题我认为还是挺不好理解的,我自己刷的时候也思考了很久,这个问题转换是这道题的核心,需要注意的三个点必须理清楚(尤其是必须从第k大的元素开始计算,还有两个数组所选取元素的索引是一样的),建议读者反复观看

这道题我没见过的点是:想要对一个数组进行排序,但是又想让其对应的索引不变,就创建一个索引数组,让这个索引数组按照待排序数组的元素大小,升序或者降序排列,这样就把num2数组排序后的结果,映射到了ids数组中

相关推荐
小蒋学算法6 分钟前
算法-阶乘函数后K个零
算法
weixin_3077791313 分钟前
智能模拟数据生成平台:生成式AI合成数据技术重塑开发测试效能
人工智能·测试工具·算法·测试用例
Darling噜啦啦17 分钟前
JavaScript 数组深度解析:从纯函数到二维数组陷阱,一文吃透前端数据结构核心
前端·javascript·数据结构
不负岁月无痕34 分钟前
C++ 模板核心内容与高频面试题汇总
java·开发语言·c++
Flittly36 分钟前
【AgentScope Java新手村系列】(2)第一个Agent-基础对话
java·spring boot·spring·ai
摇滚侠1 小时前
Spring MVC 不是一个单独的框架,是 Spring 框架的一个模块
java·spring·mvc
阿正的梦工坊1 小时前
【Rust】04-借用、引用与切片
java·数据库·rust
羊羊小栈1 小时前
Uplift营销供应链协同决策系统(基于Uplift因果推断与运筹优化算法)
前端·人工智能·算法·毕业设计·大作业
devilnumber1 小时前
静态代理 & 动态代理:实战运用 + 场景区别 + 怎么选
java·开发语言·代理模式
Upsy-Daisy1 小时前
Hermes Agent 学习笔记 02:安装、配置与第一次运行
java·前端·数据库