贪心与优先队列相结合:如何高效求解最大子序列分数

贪心与优先队列相结合:如何高效求解最大子序列分数

在这篇文章中,我们将详细讲解如何从两个长度相同的数组 nums1nums2 中选择 k 个元素的子序列,从而找到最大子序列分数。这道题目要求我们从 nums1nums2 中分别选择相同索引的 k 个元素,然后计算 nums1 中选定元素的和,并乘以它们在 nums2 中的最小值。最后返回所有可能子序列组合中的最大分数。

题目链接:LeetCode 2542 - 最大子序列的分数


问题分析

1. 题目理解与示例

给定两个相同长度的数组 nums1nums2,目标是选择 k 个元素的子序列,计算这些元素在 nums1 中的和,并乘以它们在 nums2 中的最小值,最后找到所有可能组合中分数的最大值。

举例说明:

  • 示例 1:

    输入: nums1 = [1,3,3,2], nums2 = [2,1,3,4], k = 3
    输出: 24
    解释: 选择子序列为 [3,3,2],对应 nums2 的最小值为 2,则分数为 (3+3+2)*2 = 24。
    
  • 示例 2:

    输入: nums1 = [4,2,3,1,1], nums2 = [7,5,10,9,6], k = 1
    输出: 30
    解释: 选择 nums1 中的任意一个元素,乘以其对应 nums2 中的值即可得到最大分数。
    
2. 思路分析

为了更好地理解问题,我们需要考虑几个方面:

  1. 如何选择子序列?

    • nums1 中选择 k 个元素,并对应在 nums2 中选择同样索引的元素。
  2. 如何最大化分数?

    • 分数计算为 nums1 选择 元素的和 sum 乘以 nums2 中选定元素的 最小值 min(nums2_selected)
    • 因此,我们希望尽量选择 nums1 中较大的 k 个数,同时选择 nums2 中最小值较大的组合。
  3. 复杂度与优化方向

    • 由于组合数量巨大,使用暴力枚举的方式会超时。我们需要一种高效的策略来筛选组合,从而减少计算量。
3. 常见错误与优化方向

如果使用暴力枚举所有可能的子序列组合,那么复杂度为 O ( ( n k ) ) = n ! k ! ( n − k ) ! O(\binom{n}{k}) = \frac{n!}{k!(n-k)!} O((kn))=k!(nk)!n!,这是指数级别的复杂度,显然不适用于较大的 nk

因此,我们需要考虑使用贪心算法和优先队列(堆)来优化解决方案。


优化解法:贪心 + 优先队列

1. 思路阐述

通过观察题目,我们可以发现以下贪心策略:

  • 我们希望 nums2 中的最小值尽可能大,因为它作为分数计算的乘数,影响最大。
  • 因此,我们可以优先选择 nums2 中较大的元素进行组合,然后在这些组合中选择 nums1 中较大的元素。

为了实现这一策略:

  1. 排序与配对:

    • 首先,我们将 nums1nums2 配对,形成 (nums2[i], nums1[i]) 的对组,然后按照 nums2 的值从大到小进行排序。
    • 这样一来,我们在遍历时,可以保证每次遍历到的 nums2 的值是当前所有未遍历元素中的最大值,从而尽可能增加分数中的乘数值。
  2. 优先队列维护最大 knums1

    • 使用一个优先队列(最小堆)来维护当前 nums1 中的前 k 大元素的和。
    • 当堆中元素数量超过 k 个时,移除堆顶元素(最小值),保证堆中始终包含 k 个最大的 nums1 元素。
  3. 计算分数与更新结果:

    • 每次遍历到 k 个元素时,计算当前组合的分数,即 sum(nums1_selected) * min(nums2_selected)
    • 更新当前的最大分数。
2. 代码实现
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

class Solution {
public:
    long long maxScore(vector<int>& nums1, vector<int>& nums2, int k) {
        int n = nums1.size();
        
        // 1. 创建配对数组,并按 nums2 从大到小排序
        vector<pair<int, int>> pairs(n);
        for (int i = 0; i < n; ++i) {
            pairs[i] = {nums2[i], nums1[i]};
        }
        sort(pairs.rbegin(), pairs.rend()); // 按照 nums2 降序排列

        // 2. 使用最小堆来维护 nums1 中最大的 k 个数
        priority_queue<int, vector<int>, greater<int>> minHeap;
        long long sum = 0, result = 0;

        // 3. 遍历排序后的数组
        for (int i = 0; i < n; ++i) {
            int num1 = pairs[i].second;
            int num2 = pairs[i].first;
            
            // 将当前 num1 加入堆中
            minHeap.push(num1);
            sum += num1;
            
            // 如果堆中元素超过了 k 个,移除最小的那个
            if (minHeap.size() > k) {
                sum -= minHeap.top();
                minHeap.pop();
            }
            
            // 如果堆中的元素正好是 k 个,计算当前的分数
            if (minHeap.size() == k) {
                result = max(result, sum * num2); // 当前 sum * nums2 中的最小值
            }
        }

        return result;
    }
};
3. 代码详解
  1. 排序操作:

    • nums1nums2 的元素进行配对,生成 (nums2[i], nums1[i]) 的形式,并按照 nums2 的值从大到小进行排序。
    • 排序的时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn)
  2. 优先队列维护前 k 大元素:

    • 使用最小堆(优先队列)来维护当前选定的 knums1 元素的和。
    • 每次加入一个新的元素时,如果堆中元素超过 k,则移除堆顶最小元素,以保证堆中是当前最大的 k 个元素。
  3. 分数计算与结果更新:

    • 当堆中元素达到 k 个时,计算当前组合的分数。
    • 分数等于堆中 nums1 元素的和 sum,乘以当前 nums2 中的最小值(因为是按降序排列,所以 nums2 中的最小值就是当前遍历的元素)。
4. 时间复杂度分析
  • 排序: O ( n log ⁡ n ) O(n \log n) O(nlogn)
  • 堆操作: 遍历每个元素并对堆进行插入或删除操作,每次操作的时间复杂度为 O ( log ⁡ k ) O(\log k) O(logk),因此总的时间复杂度为 O ( n log ⁡ k ) O(n \log k) O(nlogk)

总时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn),相对于暴力枚举的指数级复杂度,这种方式显著提升了算法的效率。


示例讲解

示例 1:

输入:nums1 = [1,3,3,2], nums2 = [2,1,3,4], k = 3

  1. 首先,我们对 nums2 从大到小进行排序:

    • 排序后的配对数组为 [(4, 2), (3, 3), (2, 1), (1, 3)]
  2. 使用最小堆维护 k = 3 个最大的 nums1 元素:

    • 第一个配对 (4, 2):加入堆,当前堆为 [2],和为 2,当前最小 nums24,暂时不计算。
    • 第二个配对 (3, 3):加入堆,当前堆为 [2, 3],和为 5,当前最小 nums23,暂时不计算。
    • 第三个配对 (3, 3):加入堆,当前堆为 [2, 3, 3],和为 8,此时堆中有 3 个元素,可以计算分数为 8 * 3 = 24
    • 第四个配对 (2, 1):堆已满,且此时堆顶最小元素为 2,替换后和不变,最终分数为 24

输出:24

示例 2:

输入:nums1 = [4,2,3,1,1], nums2 = [7,5,10,9,6], k = 1

  1. nums2 从大到小排序:

    • 排序后的配对数组为 [(10, 3), (9, 1), (7, 4), (6, 1), (5, 2)]
  2. 使用最小堆维护 k = 1 个最大的 nums1 元素:

    • 第一个配对 (10, 3):堆中元素为 [3],分数为 3 * 10 = 30
    • 后续配对都不比此分数大,最终结果为 30

输出:30

总结

通过贪心策略和优先队列的结合,我们能够高效解决“最大子序列的分数”这一问题。核心思路在于排序 nums2 来优先选择最大乘数,然后通过维护 nums1 中最大和的 k 个数来最大化分数。希望这篇文章能够帮助你深入理解该题目的优化方法和代码实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/887596.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

北交大研究突破:塑料光纤赋能低成本无摄像头AR/VR眼动追踪技术

北交大研究&#xff1a;探索无摄像头低成本AR/VR眼动追踪新路径 在AR/VR技术领域&#xff0c;眼动追踪作为一项关键技术&#xff0c;对于提升用户体验、优化渲染效率具有重要意义。然而&#xff0c;传统的眼动追踪方案多依赖于高成本的摄像头&#xff0c;这不仅增加了设备的制造…

Python 工具库每日推荐 【Pandas】

文章目录 引言Python数据处理库的重要性今日推荐:Pandas工具库主要功能:使用场景:安装与配置快速上手示例代码代码解释实际应用案例案例:销售数据分析案例分析高级特性数据合并和连接时间序列处理数据透视表扩展阅读与资源优缺点分析优点:缺点:总结【 已更新完 TypeScrip…

市面上8款AI论文大纲一键生成文献的软件推荐

在当前的学术研究和写作领域&#xff0c;AI论文大纲自动生成软件已经成为提高写作效率和质量的重要工具。这些工具不仅能够帮助研究人员快速生成论文草稿&#xff0c;还能进行内容优化、查重和排版等操作。本文将分享市面上8款AI论文大纲一键生成文献的软件&#xff0c;并特别推…

一文了解构建工具——Maven与Gradle的区别

目录 一、Maven和Gradle是什么&#xff1f; 构建工具介绍 Maven介绍 Gradle介绍 二、使用时的区别&#xff1a; 1、新建项目 Maven&#xff1a; Gradle&#xff1a; 2、配置项目 Maven&#xff1a; Gradle&#xff1a; 3、构建项目——生成项目的jar包 Gradle&…

用小学生可以理解的语言讲一下什么是大模型

好的&#xff0c;用小学生的语言来说&#xff0c;大模型就像是一个超级聪明的机器人老师&#xff0c;它懂得很多东西&#xff0c;可以帮助我们做很多事情。 1. **懂得很多**&#xff1a;大模型知道很多知识&#xff0c;就像一个巨大的图书馆&#xff0c;里面有很多书&#xff0…

IDEA 2024.3 预览:把开发者感动到哭了

幸运的人&#xff0c; 一生都被童年治愈&#xff1b; 不幸的人&#xff0c; 一生都在治愈童年 只有勇敢的人 和有钱的人才能先享受世界 缘分就是我还不知道 会见到你就误打误撞般 遇见了你 最近 IDEA 又发布了最新的 2024.3 的预览版本 EAP&#xff0c;把开发者的心激动的…

今日指数-day08实战完整代码

今日指数-day08 1. 个股最新分时行情数据 1.1 个股最新分时行情功能说明 1&#xff09;个股最新分时行情功能原型 2&#xff09;个股最新分时行情数据接口分析 功能描述&#xff1a;获取个股最新分时行情数据&#xff0c;主要包含&#xff1a;开盘价、前收盘价、最新价、最…

AI周报(9.29-10.5)

AI应用-Elayne公司临终规划和自动化遗产结算 创业公司Elayne成立于2023年&#xff0c;由Adria Ferrier和Jake Grafenstein共同创立&#xff0c;Adria Ferrier担任CEO&#xff0c;总部位于科罗拉多州丹佛市。 Elayne公司专注于遗产规划和结算领域&#xff0c;通过人工智能技术…

【Diffusion分割】CTS:基于一致性的医学图像分割模型

CTS: A Consistency-Based Medical Image Segmentation Model 摘要&#xff1a; 在医学图像分割任务中&#xff0c;扩散模型已显示出巨大的潜力。然而&#xff0c;主流的扩散模型存在采样次数多、预测结果慢等缺点。最近&#xff0c;作为独立生成网络的一致性模型解决了这一问…

【C++】STL——list的模拟实现

目录 前言list介绍list的模拟实现总体结构节点类迭代器类链表类 默认成员函数构造函数拷贝构造赋值重载析构函数 迭代器实现双向迭代器迭代器的其他功能用多参数模板完成最终的迭代器类 list的容量相关和数据访问empty()和size()front()和back() list的修改操作任意位置插入和删…

数据结构 ——— C语言实现无哨兵位单向不循环链表

目录 前言 动态顺序表的缺陷 单链表的概念 单链表中节点的结构 单链表逻辑结构示意图​编辑 实现单链表前的准备工作 实现单链表 1. 定义节点的指针 2. 创建节点 3. 打印单链表中的所有数据 4. 在单链表头部插入数据 5. 在单链表尾部插入数据 6. 在单链表头部删除数…

脏读、不可重复读、幻读的解决方法

上一篇博客提到了脏读、不可重复读、幻读的含义&#xff0c;也知道了是因为什么情况导致出现的这些问题&#xff0c;这篇博客就带大家一起来了解一下他们的解决办法~ 脏读&#xff1a;脏读出现的原因主要是因为一个事务读取了另外一个事务未提交的数据&#xff0c;就可能出现脏…

掌握嵌套子查询:复杂 SQL 中 * 列的准确表列关系

在日常开发中&#xff0c;我们常常需要对复杂的 SQL 进行数据血缘分析。 本文重点讨论在具有 * 列的嵌套子查询中建立表和列之间正确关系的挑战。使用 Teradata SQL 代码示例来说明该过程。 本文聚焦于一个别名为 SUBSCRIBER_ 的子查询及其派生的列&#xff0c;这些列在外层查…

无需VPN!大厂力作:免费AI对口型神器登场,让你的视频制作更简单!

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 &#xff08;偶尔会因为推荐工具&#xff…

《深度学习》OpenCV 图像拼接 原理、参数解析、案例实现

目录 一、图像拼接 1、直接看案例 图1与图2展示&#xff1a; 合并完结果&#xff1a; 2、什么是图像拼接 3、图像拼接步骤 1&#xff09;加载图像 2&#xff09;特征点检测与描述 3&#xff09;特征点匹配 4&#xff09;图像配准 5&#xff09;图像变换和拼接 6&am…

实验3 选择结构

1、计算分段函数的值 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <math.h> int main() {double x,y0;scanf("%lf",&x);if(x<0){printf("error!\n");return 0;}if(0<x&&x<1){ylog10(x);}else if(1<…

缓存数据减轻服务器压力

问题:不是所有的数据都需要请求后端的 不是所有的数据都需要请求后端的,有些数据是重复的、可以复用的解决方案:缓存 实现思路:每一个分类为一个key,一个可以下面可以有很多菜品 前端是按照分类查询的,所以我们需要通过分类来缓存缓存代码 /*** 根据分类id查询菜品** @pa…

Java | Leetcode Java题解之第459题重复的子字符串

题目&#xff1a; 题解&#xff1a; class Solution {public boolean repeatedSubstringPattern(String s) {return kmp(s s, s);}public boolean kmp(String query, String pattern) {int n query.length();int m pattern.length();int[] fail new int[m];Arrays.fill(fa…

54.二叉树的最大深度

迭代 class Solution {public int maxDepth(TreeNode root) {if(rootnull){return 0;}int de0;Queue<TreeNode> qunew LinkedList<>();TreeNode tn;int le;qu.offer(root);while(!qu.isEmpty()){lequ.size();while(le>0){tnqu.poll();if(tn.left!null){qu.offe…

学会这几个简单的bat代码,轻松在朋友面前装一波13[通俗易懂]

大家好&#xff0c;又见面了&#xff0c;我是你们的朋友全栈君。 这个标题是干什么用的? 最近看晚上某些人耍cmd耍的十分开心&#xff0c;还自称为“黑客”&#xff0c;着实比较搞笑.他们那些花里胡哨的东西在外行看来十分nb,但只要略懂一些&#xff0c;就会发现他们的那些十…