【线段树】【前缀和】:1687从仓库到码头运输箱子

本题简单解法

C++前缀和算法的应用:1687从仓库到码头运输箱子

本文涉及的基础知识点

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频
线段树

LeetCode1687从仓库到码头运输箱子

你有一辆货运卡车,你需要用这一辆车把一些箱子从仓库运送到码头。这辆卡车每次运输有 箱子数目的限制 和 总重量的限制 。
给你一个箱子数组 boxes 和三个整数 portsCount, maxBoxes 和 maxWeight ,其中 boxes[i] = [ports​​i​, weighti] 。
ports​​i 表示第 i 个箱子需要送达的码头, weightsi 是第 i 个箱子的重量。
portsCount 是码头的数目。
maxBoxes 和 maxWeight 分别是卡车每趟运输箱子数目和重量的限制。
箱子需要按照 数组顺序 运输,同时每次运输需要遵循以下步骤:
卡车从 boxes 队列中按顺序取出若干个箱子,但不能违反 maxBoxes 和 maxWeight 限制。
对于在卡车上的箱子,我们需要 按顺序 处理它们,卡车会通过 一趟行程 将最前面的箱子送到目的地码头并卸货。如果卡车已经在对应的码头,那么不需要 额外行程 ,箱子也会立马被卸货。
卡车上所有箱子都被卸货后,卡车需要 一趟行程 回到仓库,从箱子队列里再取出一些箱子。
卡车在将所有箱子运输并卸货后,最后必须回到仓库。
请你返回将所有箱子送到相应码头的 最少行程 次数。
示例 1:
输入:boxes = [[1,1],[2,1],[1,1]], portsCount = 2, maxBoxes = 3, maxWeight = 3
输出:4
解释:最优策略如下:

  • 卡车将所有箱子装上车,到达码头 1 ,然后去码头 2 ,然后再回到码头 1 ,最后回到仓库,总共需要 4 趟行程。
    所以总行程数为 4 。
    注意到第一个和第三个箱子不能同时被卸货,因为箱子需要按顺序处理(也就是第二个箱子需要先被送到码头 2 ,然后才能处理第三个箱子)。
    示例 2:
    输入:boxes = [[1,2],[3,3],[3,1],[3,1],[2,4]], portsCount = 3, maxBoxes = 3, maxWeight = 6
    输出:6
    解释:最优策略如下:
  • 卡车首先运输第一个箱子,到达码头 1 ,然后回到仓库,总共 2 趟行程。
  • 卡车运输第二、第三、第四个箱子,到达码头 3 ,然后回到仓库,总共 2 趟行程。
  • 卡车运输第五个箱子,到达码头 2 ,回到仓库,总共 2 趟行程。
    总行程数为 2 + 2 + 2 = 6 。
    示例 3:
    输入:boxes = [[1,4],[1,2],[2,1],[2,1],[3,2],[3,4]], portsCount = 3, maxBoxes = 6, maxWeight = 7
    输出:6
    解释:最优策略如下:
  • 卡车运输第一和第二个箱子,到达码头 1 ,然后回到仓库,总共 2 趟行程。
  • 卡车运输第三和第四个箱子,到达码头 2 ,然后回到仓库,总共 2 趟行程。
  • 卡车运输第五和第六个箱子,到达码头 3 ,然后回到仓库,总共 2 趟行程。
    总行程数为 2 + 2 + 2 = 6 。
    示例 4:
    输入:boxes = [[2,4],[2,5],[3,1],[3,2],[3,7],[3,1],[4,4],[1,3],[5,2]], portsCount = 5, maxBoxes = 5, maxWeight = 7
    输出:14
    解释:最优策略如下:
  • 卡车运输第一个箱子,到达码头 2 ,然后回到仓库,总共 2 趟行程。
  • 卡车运输第二个箱子,到达码头 2 ,然后回到仓库,总共 2 趟行程。
  • 卡车运输第三和第四个箱子,到达码头 3 ,然后回到仓库,总共 2 趟行程。
  • 卡车运输第五个箱子,到达码头 3 ,然后回到仓库,总共 2 趟行程。
  • 卡车运输第六和第七个箱子,到达码头 3 ,然后去码头 4 ,然后回到仓库,总共 3 趟行程。
  • 卡车运输第八和第九个箱子,到达码头 1 ,然后去码头 5 ,然后回到仓库,总共 3 趟行程。
    总行程数为 2 + 2 + 2 + 2 + 3 + 3 = 14 。

提示:
1 <= boxes.length <= 105
1 <= portsCount, maxBoxes, maxWeight <= 105
1 <= ports​​i <= portsCount
1 <= weightsi <= maxWeight

线段树

lineTree[j]表示最后一趟运输boxs[j…i-1]的最小行程数,不包括返回
boxs[left…i]可以同车而不超重,如果有多个left,取最小值。
lineTree[i] = min ⁡ x : l e f t i − 1 + 1 + 1 \min_{x:left}^{i-1}+1+1 minx:lefti1+1+1
lineTree[0…left-1]不会被使用,所以无需维护。当i++时 lineTree[j]的含义从最后一趟运输 boxs[j…i-1]变成最后一趟运行boxs[j…i]。
如果boxs[i][0]!=boxs[i-1][0],lineTree[left…i-1] 全部+1。
最后结果:[left…n-1]的最小值。
本题解用到了线段树的区间更新、区间查询。

代码

核心代码

template<class TSave, class TRecord>
class CLineTree
{
public:
	CLineTree(int iEleSize, TRecord recordNull=0)
		:m_iEleSize(iEleSize), m_vArr(m_iEleSize * 4), m_vRecord(m_iEleSize * 4, recordNull), m_recordNull(recordNull)
	{

	}
	void Update(int iLeftIndex, int iRightIndex, TRecord value)
	{
		Update(1, 1, m_iEleSize, iLeftIndex + 1, iRightIndex + 1, value);
	}
	void Query( int iLeftIndex, int iRightIndex)
	{
		Query( 1, 1, m_iEleSize, iLeftIndex + 1, iRightIndex + 1);
	}
private:
	virtual void OnQuery(TSave& save) = 0;
	virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) = 0;
	virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r) = 0;
	virtual void OnUpdate(TSave& save, const int& len, const TRecord& update) = 0;
	void Query( int iNode, int iSaveLeft, int iSaveRight, int iQueryLeft, int iQueryRight)
	{
		if ((iQueryLeft <= iSaveLeft) && (iQueryRight >= iSaveRight))
		{
			OnQuery(m_vArr[iNode]);
			return;
		}
		Fresh(iNode, iSaveLeft, iSaveRight);
		const int iMid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
		if (iMid >= iQueryLeft)
		{
			Query( iNode * 2, iSaveLeft, iMid, iQueryLeft, iQueryRight);
		}
		if (iMid + 1 <= iQueryRight)
		{
			Query( iNode * 2 + 1, iMid + 1, iSaveRight, iQueryLeft, iQueryRight);
		}
	}
	void Update(int iNode, int iSaveLeft, int iSaveRight, int iOpeLeft, int iOpeRight, TRecord value)
	{
		if (iNode >= m_vArr.size())
		{
			return;
		}
		if ((iOpeLeft <= iSaveLeft) && (iOpeRight >= iSaveRight))
		{
			OnUpdate(m_vArr[iNode], min(iSaveRight, iOpeRight) - max(iSaveLeft, iOpeLeft) + 1, value);
			OnUpdateRecord(m_vRecord[iNode], value);
			return;
		}
		Fresh(iNode, iSaveLeft, iSaveRight);
		const int iMid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
		if (iMid >= iOpeLeft)
		{
			Update(iNode * 2, iSaveLeft, iMid, iOpeLeft, iOpeRight, value);
		}
		if (iMid + 1 <= iOpeRight)
		{
			Update(iNode * 2 + 1, iMid + 1, iSaveRight, iOpeLeft, iOpeRight, value);
		}
		// 如果有后代,至少两个后代
		OnUpdateParent(m_vArr[iNode], m_vArr[iNode * 2], m_vArr[iNode * 2 + 1]);
	}
	void Fresh(int iNode, int iDataLeft, int iDataRight)
	{
		if (m_recordNull == m_vRecord[iNode])
		{
			return;
		}
		const int iMid = iDataLeft + (iDataRight - iDataLeft) / 2;
		Update(iNode * 2, iDataLeft, iMid, iDataLeft, iMid, m_vRecord[iNode]);
		Update(iNode * 2 + 1, iMid + 1, iDataRight, iMid + 1, iDataRight, m_vRecord[iNode]);
		m_vRecord[iNode] = m_recordNull;
	}
	const int m_iEleSize;
	vector<TSave> m_vArr;
	vector<TRecord> m_vRecord;
	const TRecord m_recordNull;
};

template<class TSave = int , class TRecord = int>
class CMinLineTree : public CLineTree<TSave, TRecord>
{
public:
	using CLineTree<TSave, TRecord>::CLineTree;
	int m_iQueryValue = INT_MAX;
protected:
	virtual void OnQuery(TSave& save) override
	{
		m_iQueryValue = min(m_iQueryValue, save);
	}
	virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) override
	{
		old += newRecord;
	}
	virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r) override
	{
		par = min(left, r);
	}
	virtual void OnUpdate(TSave& save, const int& len, const TRecord& update) override
	{
		save += update;
	}
	
};
 
  class Solution {
  public:
	  int boxDelivering(vector<vector<int>>& boxes, int portsCount, int maxBoxes, int maxWeight) {
		  m_c = boxes.size();
		  
		  vector<long long> vWeightSum = { 0 };//箱子重量前缀和
		  for (const auto& v : boxes)
		  {
			  vWeightSum.emplace_back(v[1] + vWeightSum.back());
		  }
		  CMinLineTree<> lineTree(m_c);//lineTree[j]表示最后一趟运输boxs[i...j-1]的最小行程数,不包括返回
		  lineTree.Update(0, 0, 1);
		  int left = 0;
		  for (int i = 1; i < m_c; i++)
		  {
			  lineTree.m_iQueryValue = INT_MAX / 10;
			  lineTree.Query(left, i - 1);
			  lineTree.Update(i, i, lineTree.m_iQueryValue + 2);
			  for (; (i - left + 1 > maxBoxes) || (vWeightSum[i + 1] - vWeightSum[left] > maxWeight); left++);
			  if ((boxes[i][0] != boxes[i - 1][0])&&( left <= i-1))
			  {
				  lineTree.Update(left, i - 1, 1);
			  }
		  }
		  lineTree.m_iQueryValue = INT_MAX / 10;
		  lineTree.Query(left, m_c - 1);
		  return lineTree.m_iQueryValue+1;
	  }
	  int m_c;
	
  };

测试用例

template
void Assert(const vector& v1, const vector& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
assert(v1[i] == v2[i]);
}
}

template
void Assert(const T& t1, const T& t2)
{
assert(t1 == t2);
}

int main()
{
vector<vector> boxes = { {1,1},{2,1},{1,1} };
int portsCount = 2, maxBoxes = 3, maxWeight = 3;
auto res = Solution().boxDelivering(boxes, portsCount, maxBoxes, maxWeight);
Assert(4, res);
boxes = { {1,2},{3,3},{3,1},{3,1},{2,4} };
portsCount = 3, maxBoxes = 3, maxWeight =6;
res = Solution().boxDelivering(boxes, portsCount, maxBoxes, maxWeight);
Assert(6, res);
boxes = { {2,4},{2,5},{3,1},{3,2},{3,7},{3,1},{4,4},{1,3},{5,2} };
portsCount = 5, maxBoxes = 5, maxWeight = 7;
res = Solution().boxDelivering(boxes, portsCount, maxBoxes, maxWeight);
Assert(14, res);

//CConsole::Out(res);

}

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《闻缺陷则喜算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

鄙人想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
墨家名称的来源:有所得以墨记之。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17

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

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

相关文章

【Vue】响应式原理与ref

首先讲讲JS中的Proxy JavaScript 运行环境包含了一些不可枚举、不可写入的对象属性&#xff0c;然而在 ES5 之前开发者无法定义他们自己的不可枚举属性或不可写入属性。ES5 引入 Object.defineProperty() 方法以便开发者在这方面能够像 JS 引擎那样做。 ES6 为了让开发者能进…

阿里云服务器可以干嘛?阿里云服务器八大用途介绍

阿里云服务器可以干嘛&#xff1f;能干啥你还不知道么&#xff01;简单来讲可用来搭建网站、个人博客、企业官网、论坛、电子商务、AI、LLM大语言模型、测试环境等&#xff0c;阿里云百科aliyunbaike.com整理阿里云服务器的用途&#xff1a; 阿里云服务器活动 aliyunbaike.com…

springboot实现上传文件接口(简单版)

使用springboot实现一个最简单版本的上传文件接口 private String uploadPath "C:/imageFiles";RequestMapping(value "/upload", method RequestMethod.POST)private Result upload( RequestParam("modelName") String modelName,RequestPar…

前端实现拖拽div修改宽度,用于菜单栏等多处场景

1、演示 2、 比较简单&#xff0c;直接上源码&#xff08;内部有注释&#xff09; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-…

鸿蒙内核源码分析 (并发并行篇) | 内核如何管理多个 CPU?

理解并发概念 并发&#xff08;Concurrent&#xff09;: 多个线程在单个核心运行&#xff0c;同一时间只能一个线程运行&#xff0c;内核不停切换线程&#xff0c;看起来像同时运行&#xff0c;实际上是线程被高速的切换. 通俗好理解的比喻就是高速单行道&#xff0c;单行道指…

【canvas】canvas的基础使用(一):创建canvas

canvas Canvas API 提供了一个通过JavaScript 和 HTML的<canvas>元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。 Canvas API 主要聚焦于 2D 图形。 canvas元素 <canvas> 元素可被用来通过 JavaScript&#xff…

【Figma】安装指南及基础操作

先前做UI设计一直都是用PS等绘图软件设计&#xff0c;但发现在纠结像素和排版问题上会花很多时间&#xff0c;再加上AI没来得及上手&#xff0c;就需要迅速出成图&#xff0c;此时通过论坛发现了figma&#xff0c;基本上可以满足足够的需求&#xff0c;并且可以在windows系统上…

程序员如何搞副业

#程序员如何搞副业&#xff1f;# 在快速发展的IT行业中&#xff0c;程序员作为技术骨干&#xff0c;通常拥有扎实的编程能力和丰富的项目经验。然而&#xff0c;随着职业生涯的深入&#xff0c;许多程序员开始思考如何进一步提升自我价值&#xff0c;实现更多的经济收益。副业成…

身份证实名认证接口的价格一般是多少呢?基于PHP身份核验接口

身份证实名认证接口分为身份证二要素、三要素、三要素人像核验接口&#xff0c;被广泛的应用于婚恋、交友、电商等等一系列行业领域&#xff0c;身份证实名认证需要实时数据&#xff0c;对于数据源来说也需要可靠&#xff0c;那么&#xff0c;身份证实名认证的价格是不是很贵呢…

Yolov8-pose关键点检测:特征融合 | CAMixing:卷积-注意融合模块和多尺度提取能力 | 2024年4月最新成果

💡💡💡本文独家改进:CAMixingBlock更好的提取全局上下文信息和局部特征,包括两个部分:卷积-注意融合模块和多尺度前馈网络; 💡💡💡如何跟YOLOv8结合:1)放在backbone后增强对全局和局部特征的提取能力;2)放在detect前面,增强detect提取能力; 提供多种改进方…

HarmonyOS实战开发-如何使用调用系统任务管理的能力。

介绍 本示例通过调用系统任务管理的能力&#xff0c;使用ohos.application.missionManager 、ohos.multimedia.image 等接口完成对系统任务执行锁定、解锁、清理、切换到前台等操作。 效果预览 使用说明 1.下拉“获取系统任务信息”&#xff0c;获取当前系统任务的包名、运行…

简单用Nodejs + express 编写接口

文章目录 get接口示范post接口示范注意点 准备工作可以看上一篇文章&#xff1a;文章链接》》 get接口示范 app.get(/, (req, res) > {res.send("Hello World"); })因为是get接口&#xff0c;所以可以直接在浏览器上请求&#xff08;端口地址接口名&#xff09;…

如何快速摸清一个行业?

作为一名职场人&#xff0c;或多或少都会遇到需要了解自己不熟悉的行业&#xff0c;比如&#xff1a; 职业选择&#xff0c;跳槽换工作时&#xff1a;哪家企业所在的行业有优势&#xff0c;未来会有更多的机会&#xff1f;哪个行业给的薪资会更高&#xff1f;行业内当下及未来的…

linux/centos/ubuntu无法开机数据恢复完美解决

生产紧急问题可私信博主解决 背景&#xff1a; 朋友跟我说他的centos7服务器开不开机了&#xff0c;给了我一张图片 他告诉运维通过xfs_repair修复无效&#xff0c;依旧启动不了 原因分析 从以上图片可知系统进入了dracut模式&#xff0c;很明显的报错/dev/mapper/centos-r…

FebHost:墨西哥.MX域名争议解决机制重要性

在全球化的浪潮中&#xff0c;有效的域名争议解决机制对于保护商标持有者的权益、防止域名系统的滥用至关重要。墨西哥&#xff0c;与其他国家一样&#xff0c;建立了一套完善的争议解决框架&#xff0c;确保商标所有者在维护其知识产权完整性时能够得到公平对待。这不仅增强了…

怎样恢复已删除的照片?教你3个方法,一键恢复!

很多人喜欢以拍照的形式记录生活&#xff0c;手机里的照片就很容易堆积成山&#xff0c;但当内存不够用时就不得不选择删除。可是这些美好的照片始终是很多人心中抹不去的记忆&#xff0c;那么该怎样恢复已删除的照片呢&#xff1f;下面几招&#xff0c;教你一键恢复&#xff0…

3.网络编程-TCP

目录 TCP 建立连接的过程是怎样的 TCP为什么是三次握手 TCP 断开连接的过程是怎样的 TCP挥手为什么需要四次 为什么TIME_WAIT等待的时间是2MSL TCP详解之滑动窗口 TCP 半连接队列和全连接队列是什么 TCP粘包&#xff0c;拆包是怎么发生的&#xff0c;如何解决 TCP是如何…

LeetCode-热题100:K 个一组翻转链表

题目描述 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节…

小白能看懂的CyberRT学习笔记

0. 简介 Apollo Cyber RT 是专为自动驾驶场景设计的开源、高性能运行时框架。 基于中心化计算模型&#xff0c;主要价值是提升自动驾驶系统的高并发、低延迟、高吞吐。 Apollo 并不是一开始就使用 CyberRT&#xff0c;在 v3.0 之前用的都是基于 ROS 框架进行开发。但在之前的…

SQL Server详细安装使用教程

1.安装环境 现阶段基本不用SQL Server数据库了&#xff0c;看到有这样的分析话题&#xff0c;就把多年前的存货发一下&#xff0c;大家也可以讨论看看&#xff0c;思路上希望还有价值。 SQL Server 2008 R2有32位版本和64位版本&#xff0c;32位版本可以安装在Windows XP及以上…