算法题:求所需的最小的书包数量(拓展拓展再拓展~)

算法题:求所需的最小的书包数量

现在有一种书包,这种书包只有两个书槽(即最多只能放下两本书。),并且一个这种书包只能装下N千克的书。现在有一个数组,数组元素是每本书的重量(千克)。问:完全装下这堆书,需要多少个书包?

题解如下:

正向暴力法

解析:

  • 首先,很容易能想到的一个解法就是暴力解法。

    贪心思想:我们先让两本最重的书先结合!

    1. 先倒序。

    2. 让最重的那个与后面的所有书逐个比较,看看它能与后面的哪本书结合,放到一个书包里面。

    (这样就能够保证最重的和较重的先被放进书包,后面轻的就更不在话下了!)

 
class Solution1 {
public:int GetBagNum(int bagLimit, vector<int> &books) {sort(books.begin(), books.end(), greater<>());vector<bool> inBags(books.size(), false); // 表示当前的书是否被装入书包了。int bagNum = 0;for (int i = books.size() - 1; i >= 0; i--) {if (inBags[i]) {    // 表示已经被装入书包continue;}for (int j = i - 1; j >= 0; j --) {if (!inBags[j] && books[i] + books[j] <= bagLimit) {    // 表示 i 和 j 可以装入同一个书包inBags[i] = true;inBags[j] = true;bagNum ++;break;}}if (!inBags[i]) { // 表示无法与后面的所有书一起放进书包,只能自己单独放bagNum ++;}}return bagNum;}
};

明显,出现O(n²)的算法是无法跑过所有用例的。

所以,我们要想出个办法,如何才能化简掉没有必要的比较操作呢?

仔细一看,还真的难想出来。因为感觉每一次比较都是必要的,要不然没法让较大的两本书放进书包。

从开头想,没办法化简,那就从尾想

从开头想(正向):正是上面的正向暴力法(让两个大的先放进去)

从末尾想(方向):让最大的跟最小的先放进去。

双指针法

  • 让最大的和最小的结合。(因为对最小的来说,最极限的情况就是和最大相结合。也就说,对最小的书来说,它非常贪心,老是想和最大的书结合放进书包。)

方法:

  1. 先倒序;

  2. 左指针向右遍历,右指针向左遍历。

class Solution2 {
public:int GetBagNum(int bagLimit, vector<int> &books) {sort(books.begin(), books.end(), greater<>());int left = 0;int right = books.size() - 1;int bagNum = 0;while (left < right) {if (books[left] + books[right] <= bagLimit) {// 这两本放到一起bagNum ++;left ++;right --;} else {// 左边的那本单独放left ++;bagNum ++;}}return bagNum;}
};

这样,就将时间复杂度降到了O(n)。可以通过所有用例了!

思维拓展:

假如题目变种为:

现在有一种书包,这种书包只有两个槽位(即最多只能放下两件物品。),并且一个这种书包只能装下N千克的物品。现在有两种物品,一种物品A,一种物品B。一个书包内【不能放两个物品A】,但是【可以放一个A、一个B】或者【直接放两个物品B】。给你两个数组,数组1为物品A的重量数组;数组2为物品B的重量数组。数组元素是每个物品的重量(千克)。问:完全装下这堆物品,需要多少个书包?

明显这道题也是双指针就能解决。

class Solution3 {
public:int GetBagNum(int bagLimit, vector<int> &booksA, vector<int> &booksB) {sort(booksA.begin(), booksA.end()); // 升序排列sort(booksB.begin(), booksB.end(), greater<>()); // 降序排列
​// 对于轻的A来说,它的贪心就是和最大的B结合放进书包int leftA = 0;int leftB = 0;int bagNum = 0;vector<bool> BInBag(booksB.size(), false); // 表示B物品是否被装进书包while (leftA < booksA.size() && leftB < booksB.size()) {if (booksA[leftA] + booksB[leftB] <= bagLimit) {BInBag[leftB] = true;bagNum ++;leftA ++;leftB ++;} else {leftB ++;}}bagNum += (booksA.size() - leftA); // 剩余的A物品是没法和物品B的结合的,只能单独装入书包。// 以下让剩余没装入的B使用双指针继续装入。int left = 0;int right = booksB.size() - 1;while (left < right) {if (booksB[left]) { // 跳过已经被装入的left ++;   continue;} if (booksB[right]) {    // 跳过已经被装入的right --;continue;}if (booksB[left] + booksB[right] <= bagLimit) {// 这两个放到一起bagNum ++;left ++;right --;} else {// 左边的那个单独放left ++;bagNum ++;}}
​return bagNum;}
};

贪心+小根堆

假如我们还是坚持要用原来那种贪心:总是让两个大的先结合。

从头想过了,无法化简;从尾想过了,还好,时间复杂度下降了;还有一个地方,可以从中间想

我们再来继续仔细看看这张图,对于11来说,它既得跟17相加比较一把,还得跟13相加比较一把。有没有一种可能?让11直接跟13相加比较一把,发现不行——> 那么可以直接得出结论:11跟17是没法结合的。(因为17比13大)

也就是说,我们可以把17和13这些比较大的书转到一个容器里面,让容器里面最小的跟11相加比较,如果可以,那就装入书包;如果不行,11和容器里面的每一个都不能相结合(因为11已经和容器里面最小的相结合比较过了)

  • 可以获取到最小元素的容器,那就只有小根堆了

但是我们要怎么把书装进小根堆呢?(什么情况装进去?)

  • 很明显,当我们遍历到一本书,并且此书没法和堆顶元素相结合的时候,就要将其加进小根堆了。(加进小根堆,看看后续的遍历元素有没有可能再和当前的书)

class Solution {
public:int GetBagNum(int bagLimit, vector<int> &books) {sort(books.begin(), books.end(), greater<>());
​priority_queue<int, vector<int>, greater<>> bigBook;
​int ret = 0;
​for (auto &cur: books) {if (bigBook.empty() || bigBook.top() + cur > bagLimit) {    // 没法和最小的堆顶元素结合,那就加入堆。bigBook.push(cur);} else {bigBook.pop();  // 如果可以和堆顶元素结合,那就将堆顶元素弹出来让其与当前元素结合。ret ++;}}return ret + bigBook.size();    // 剩余在堆里面的,都是没法和其他元素结合的。}
};
  • 一个疑问:为什么和堆顶元素结合就直接结合了?为什么不和堆里面的其他元素再比较一下,看看有没有可能和更大的元素结合?

    答案:没必要,数组后面还有更小的元素,更小的元素会和堆内其他元素结合的。

思维拓展:

假如题目变成这样:

现在有一种书包,这种书包只有3个书槽(即最多只能放下3本书。),并且一个这种书包只能装下N千克的书。现在有一个数组,数组元素是每本书的重量(千克)。问:完全装下这堆书,需要多少个书包?

题目如下,核心思想是一致的。都是先跟大的里面的小的比。(通过维护两个堆来完成这一功能)

class Solution4 {
public:int GetBagNum(int bagLimit, vector<int> &books) {sort(books.begin(), books.end(), greater<>());
​priority_queue<int, vector<int>, greater<>> pq1Book;    // 代表只有1本书的小根堆
​priority_queue<int, vector<int>, greater<>> pq2Book;    // 代表2本书的和的小根堆
​int ret = 0;
​for (auto &cur: books) {if (pq2Book.empty()) {if (pq1Book.empty()) {pq1Book.push(cur);} else {if (cur + pq1Book.top() <= bagLimit) {// 也就是说这两本书能够结合,能结合就放到堆2放着。int cur_top = pq1Book.top();pq1Book.pop();int sum = cur_top + cur;pq2Book.push(sum);} else {// 如果不能结合,就分开,都放到堆1pq1Book.push(cur);}}} else {if (cur + pq2Book.top() <= bagLimit) { // 直接和堆2顶的两本书结合。ret ++;pq2Book.pop();} else {    // 没法和堆2顶的任意两本书结合,只能到堆1碰碰运气if (cur + pq1Book.top() <= bagLimit) {// 也就是说这两本书能够结合,能结合就放到堆2放着。int cur_top = pq1Book.top();pq1Book.pop();int sum = cur_top + cur;pq2Book.push(sum);} else {// 如果不能结合,就分开,放到堆1pq1Book.push(cur);}}}}// 剩余的两个堆里面的书都是没法相互结合的。前面已经判断过了。return ret + pq2Book.size() + pq1Book.size();}
};

总结

贪心就是排序!

贪心就是排序!

贪心就是排序!

将排序用好,就能解决所有的贪心问题!

此处的排序不仅仅是sort!还包括堆priority_queue、红黑树map这两个有序数据结构。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JIRA 基本使用

该页面可以&#xff1a; 查看个人基本信息以及归属的邮件组修改常用参数配置查看指给自己的 Open 问题查看自己最近的活动记录等 权限管理 Project 权限管理 JIRA 项目有三种通用权限方案&#xff1a; 公开权限方案&#xff08;默认禁止使用此方案&#xff09;&#xff1a…

nodejs微信小程序+python+PHP学科建设管理信息系统的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

Vue3 Router跳转传参

最近遇到这个问题router跳转传参&#xff0c;真是要了老命了。 根据网上各位大神给出的方法&#xff0c;试了 import { useRouter } from vue-routerconst router useRouter()//1. 无法跳转 router.push(name:,params:{})//2. 可以跳转, 但需要在定义router同时定义占位符&a…

全栈冲刺 之 一天速成MySQL

一、为什么使用数据库 数据储存在哪里&#xff1f; 硬盘、网盘、U盘、光盘、内存&#xff08;临时存储&#xff09; 数据持久化 使用文件来进行存储&#xff0c;数据库也是一种文件&#xff0c;像excel &#xff0c;xml 这些都可以进行数据的存储&#xff0c;但大量数据操作…

高端网站设计公司 -蓝蓝设计数据可视化大屏服务

UI设计公司-蓝蓝设计&#xff08;北京兰亭妙微科技有限公司&#xff09;是一支由清华美院毕业的专业团队组成的设计公司。我们的设计师们在大屏科研信息软件UI设计领域拥有多年的工作经验和丰富的行业知识。我们对设计充满热爱&#xff0c;设计不仅是我们的专业和职业&#xff…

行内元素和块级元素分别有哪些?有何区别?怎样转换?

行内元素和块级元素分别有哪些&#xff1f; 常见的块级元素&#xff1a; p、div、form、ul、li、ol、table、h1、h2、h3、h4、h5、h6、dl、dt、dd 常见的行级元素&#xff1a; span、a、img、button、input、select 有何区别&#xff1f; 块级元素&#xff1a; 总是在新行上…

[二分查找]LeetCode1964:找出到每个位置为止最长的有效障碍赛跑路线

本文涉及的基础知识点 二分查找算法合集 作者推荐 动态规划LeetCode2552&#xff1a;优化了6版的1324模式 题目 你打算构建一些障碍赛跑路线。给你一个 下标从 0 开始 的整数数组 obstacles &#xff0c;数组长度为 n &#xff0c;其中 obstacles[i] 表示第 i 个障碍的高度…

flutter开发实战-实现获取视频的缩略图封面video_thumbnail

flutter开发实战-实现获取视频的缩略图封面video_thumbnail 在很多时候&#xff0c;我们查看视频的时候&#xff0c;视频没有播放时候&#xff0c;会显示一张封面&#xff0c;可能封面没有配置图片&#xff0c;这时候就需要通过获取视频的缩略图来显示封面了。这里使用了video…

第0篇红队笔记-APT-HTB

nmap 80 port-web尝试 searchploit-无结果 资源隐写查看-无结果 135 port rpcclient rpcinfo.py rpcdump.py rpcmap.py rpcmap.py爆破UUID 查看该UUID的表代表的服务能搜到UUID的漏洞 IOXIDResolver提取IPv6地址 IPV6-nmap smb smb探测目录 文件下载 测试其他目录 zip文件…

Quirks(怪癖)模式是什么?它和 Standards(标准)模式有什么区别?

前言: "Quirks模式"和"Standards模式"是与HTML文档渲染模式相关的两种模式。它们影响着浏览器如何解释和渲染HTML和CSS。理解它们之间的区别对于前端开发者和网页设计师来说是至关重要的。本文将深入讨论Quirks模式和Standards模式的区别&#xff0c;以及它…

vscode如何在没有网络的情况下安装插件

vscode如何在没有网络的情况下安装插件 start 遇到没有网络的电脑&#xff0c;无法直接从插件市场安装vscode的插件。写一下 vscode 插件离线安装的方法. 解决方案 目标电脑没有可以安装插件的网络&#xff0c;那我们只能在有网络的环境下载好我们的插件。然后拷贝软件到无…

一文解决msxml3.dll文件缺失问题,快速修复msxml3.dll

在了解问题之前&#xff0c;我们必须首先清楚msxml3.dll到底是什么。DLL&#xff08;Dynamic Link Libraries&#xff09;文件是Windows操作系统使用的一个重要组成部分&#xff0c;用于存储执行特定操作或任务的代码和数据。msxml3.dll为Windows系统提供处理XML文档的功能。如…

键盘打字盲打练习系列之指法练习——2

一.欢迎来到我的酒馆 盲打&#xff0c;指法练习&#xff01; 目录 一.欢迎来到我的酒馆二.开始练习 二.开始练习 前面一个章节简单地介绍了基准键位、字母键位和数字符号键位指法&#xff0c;在这个章节详细介绍指法。有了前面的章节的基础练习&#xff0c;相信大家对盲打也有了…

Ubuntu 2204 安装libimobiledevice

libimobiledevice是一个开源的软件&#xff0c;它可以直接使用系统原生协议和IOS设备进行通信&#xff0c;类似iMazing&#xff0c;iTunes&#xff0c;libimobiledevice不依赖IOS的私有库&#xff0c;并且连接IOS设备时用的都是原生协议&#xff0c;IOS无需越狱就能实现设备信息…

贝斯手-MISC-bugku-解题步骤

——CTF解题专栏—— 题目信息&#xff1a; 题目&#xff1a;贝斯手 作者&#xff1a;Tokeii 提示&#xff1a;无 解题附件&#xff1a; 解题思路&信息收集&#xff1a; 详细信息看了&#xff0c;没有藏料&#xff0c;这次上来就是一个命好名的压缩包浅浅打开一下&…

9.整数转换为布尔值【2023.12.1】

1.问题描述 整数转换为布尔值。 2.解决思路 输入一个整数。 输出布尔值并输出。 3.代码实现 numint(input("请输入一个数字")) boolnumbool(num) print(boolnum)4.运行结果

AutoDL 使用记录

AutoDL 使用记录 1.租用新实例 创建实例需要依次选择&#xff1a;计费方式 → \to → 地区 → \to → GPU型号与数量 → \to → 主机 注意事项&#xff1a; 主机 ID&#xff1a;一个吉利的机号有助于炼丹成功价格&#xff1a;哪个便宜选哪个最高 CUDA 版本&#xff1a;影响…

ElasticSearch知识体系详解

1.介绍 ElasticSearch是基于Lucene的开源搜索及分析引擎&#xff0c;使用Java语言开发的搜索引擎库类&#xff0c;并作为Apache许可条款下的开放源码发布&#xff0c;是当前流行的企业级搜索引擎。 它可以被下面这样准确的形容&#xff1a; 一个分布式的实时文档存储&#xf…

API成批分配漏洞介绍与解决方案

一、API成批分配漏洞介绍 批量分配&#xff1a;在API的业务对象或数据结构中&#xff0c;通常存在多个属性&#xff0c;攻击者通过篡改属性值的方式&#xff0c;达到攻击目的。比如通过设置user.is_admin和user.is_manager的值提升用户权限等级&#xff1b;假设某API的默认接口…

SQL Server 数据库,使用函数查询统计信息

4.1 在查询中使用函数 在前面章节已经学习了一些简单的增、删、改、查询的T-SOL.语句&#xff0c;但是为了更方便快捷地完 成大量的任务&#xff0c;SOLServer提供了一些内部函数&#xff0c;可以和SOLServer的SELECT语句联合使用&#xff0c;也可 以与UPDATE和INSERT一起使用&…