当前位置: 首页 > article >正文

动态规划专题(14):石子合并问题(未完待续)

问题描述一群小孩子在玩小石子游戏游戏有两种玩法。1路边玩法有n堆石子堆放在路边将石子有序地合并成一堆每次只能移动相邻的两堆石子合并合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小或最大。2操场玩法一个圆形操场周围摆放着n堆石子将石子有序地合并成一堆每次只能移动相邻的两堆石子合并合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小或最大。一、概念介绍石子合并问题是经典的动态规划Dynamic Programming, DP问题分为两种场景1. 路边玩法线性排列有 n堆石子线性排列如路边的一排石子堆每次只能合并相邻的两堆石子。合并花费为新堆的石子总数要求将所有石子合并成一堆时的最小/最大总花费。2. 操场玩法环形排列有 n堆石子环形排列如圆形操场周围每次只能合并相邻的两堆石子。合并花费为新堆的石子总数要求将所有石子合并成一堆时的最小/最大总花费。二、适用场景与距离1. 适用场景线性排列任务调度如合并连续的任务段花费为任务总量、区间合并如合并相邻的数组段花费为元素和等。环形排列环形任务调度如环形生产线上的工序合并、环形资源分配如环形农田的作物合并等。2. 场景距离复杂度对比线性排列路边时间复杂度 O(n3)朴素DP或 O(n2)优化DP。环形排列操场需将环形拆分为线性复制数组时间复杂度 O(n3)朴素或 O(n2)优化。三、理解方法1. 核心思想动态规划区间DP状态定义设 dp[i][j]为合并第 i堆到第 j堆石子的最小/最大总花费。状态转移线性排列dp[i][j]min/maxkij−1​{dp[i][k]dp[k1][j]sum(i,j)}其中 sum(i,j)是 i到 j堆的石子总数。环形排列将数组复制一份长度 2n转化为线性问题取 dp[1][n],dp[2][n1],…,dp[n][2n−1]中的最小/最大值。初始条件dp[i][i]0单堆石子无需合并花费为0。2. 朴素 vs 优化直观理解朴素方法直接枚举所有区间 [i,j]和分割点 k计算所有可能的转移逻辑清晰但效率低。优化方法预处理前缀和快速计算 sum(i,j)。环形转线性避免重复计算。剪枝或单调性优化减少无效转移。四、使用技巧1. 前缀和优化预处理前缀和数组 s其中 s[0]0s[i]s[i−1]a[i]a为石子数数组。则 sum(i,j)s[j]−s[i−1]将求和复杂度从 O(n)降为 O(1)。2. 环形转线性对于环形问题构造长度为 2n的数组 b[a1​,a2​,…,an​,a1​,a2​,…,an​]然后对 b计算所有长度为 n的区间 [i,in−1]的 dp[i][in−1]最终取最小值/最大值。3. 空间优化可选对于线性问题若只需最终结果可将二维DP数组压缩为一维但需注意转移顺序。五、细节注意1. 边界条件单堆石子dp[i][i]0。两堆石子直接合并花费为 a[i]a[j]。2. 数组索引前缀和数组 s的索引从 0开始石子数组 a从 1开始避免越界。环形转线性时确保新数组长度为 2n且区间长度为 n。3. 数据类型石子总数和花费可能很大需用long long类型防止溢出。六、问题避免1. 重复计算未用前缀和每次计算 sum(i,j)都遍历数组导致时间复杂度从 O(n2)升至 O(n3)。环形未转线性直接处理环形会漏解或重复计算。2. 溢出错误石子数较大时用int存储会溢出需用long long。3. 逻辑错误状态转移时忘记加 sum(i,j)合并后新堆的花费。环形转线性时区间长度错误应为 n而非 2n。七、总结石子合并问题是动态规划的经典应用核心在于区间划分和状态转移。线性排列是基础环形排列通过“复制数组”转化为线性问题。优化方向主要是前缀和加速和空间/时间复杂度优化。理解时需抓住“相邻合并”和“总花费子问题花费当前合并花费”的核心逻辑。石子合并问题的C实现线性环形实例代码1朴素动态规划线性排列最小花费#include iostream #include vector #include climits using namespace std; // 线性排列最小合并花费 long long minCostLinear(vectorint a) { int n a.size(); vectorvectorlong long dp(n, vectorlong long(n, 0)); vectorlong long s(n 1, 0); // 前缀和s[0]0, s[i]a[0]...a[i-1] // 计算前缀和 for (int i 1; i n; i) { s[i] s[i - 1] a[i - 1]; } // 区间长度从2到n单堆花费为0无需处理 for (int len 2; len n; len) { for (int i 0; i n - len; i) { // 区间起点i终点jilen-1 int j i len - 1; dp[i][j] LLONG_MAX; // 初始化为最大值 for (int k i; k j; k) { // 分割点k合并[i,k]和[k1,j] dp[i][j] min(dp[i][j], dp[i][k] dp[k 1][j] (s[j 1] - s[i])); } } } return dp[0][n - 1]; } int main() { // 测试数据1n4, [1,2,3,4] vectorint a1 {1, 2, 3, 4}; cout 测试1线性最小: minCostLinear(a1) endl; // 应输出19 // 测试数据2n3, [3,4,5] vectorint a2 {3, 4, 5}; cout 测试2线性最小: minCostLinear(a2) endl; // 应输出24 // 测试数据3n2, [5,6] vectorint a3 {5, 6}; cout 测试3线性最小: minCostLinear(a3) endl; // 应输出11 return 0; }实例代码2优化动态规划环形排列最小花费#include iostream #include vector #include climits using namespace std; // 环形排列最小合并花费转为线性 long long minCostCircular(vectorint a) { int n a.size(); vectorint b(a.begin(), a.end()); b.insert(b.end(), a.begin(), a.end()); // 复制一份转为长度2n的数组 vectorvectorlong long dp(2 * n, vectorlong long(2 * n, 0)); vectorlong long s(2 * n 1, 0); // 前缀和s[0]0, s[i]b[0]...b[i-1] // 计算前缀和 for (int i 1; i 2 * n; i) { s[i] s[i - 1] b[i - 1]; } long long res LLONG_MAX; // 区间长度为n环形转线性后取所有长度为n的区间 for (int len 2; len n; len) { for (int i 0; i 2 * n - len; i) { int j i len - 1; dp[i][j] LLONG_MAX; for (int k i; k j; k) { dp[i][j] min(dp[i][j], dp[i][k] dp[k 1][j] (s[j 1] - s[i])); } } } // 取所有长度为n的区间的最小值 for (int i 0; i n; i) { res min(res, dp[i][i n - 1]); } return res; } int main() { // 测试数据1n4, [1,2,3,4]环形最小应为20不线性是19环形需看分割 // 实际环形测试n4, [1,2,3,4] → 线性是19环形转线性后可能的最小是19 vectorint a1 {1, 2, 3, 4}; cout 测试1环形最小: minCostCircular(a1) endl; // 应输出19若环形不影响 // 测试数据2n3, [3,4,5]线性是24环形转线性后取[i,i2]i0,1,2 → 结果相同 vectorint a2 {3, 4, 5}; cout 测试2 环形最小: minCostCircular(a2) endl; // 应输出24 // 测试数据3n2, [5,6]环形和线性相同 vectorint a3 {5, 6}; cout 测试3环形最小: minCostCircular(a3) endl; // 应输出11 // 新增测试n4, [2,3,4,1]环形测试 vectorint a4 {2, 3, 4, 1}; cout 测试4环形最小: minCostCircular(a4) endl; // 需计算示例输出可能为20 return 0; }测试数据与输出说明1. 线性排列测试实例代码1测试1输入[1,2,3,4]输出19合并顺序1233366410不正确顺序是 (12)3花费3(33)6花费6(64)10花费10总花费361019或 (23)5花费5(15)6花费6(64)10花费10→ 总561021哦朴素DP会自动找最小正确计算应为区间长度2(0,1)3, (1,2)7, (2,3)12区间长度3(0,2)37616不前缀和s[3]6所以dp[0][2] min(dp[0][0]dp[1][2]6, dp[0][1]dp[2][2]6) → min(07613, 3069) → 9然后区间长度4dp[0][3] min(dp[0][0]dp[1][3]10, dp[0][1]dp[2][3]10, dp[0][2]dp[3][3]10) → min(0191029, 3121025, 901019) → 19。所以输出19。测试2输入[3,4,5]输出24合并顺序347花费77512花费12→ 总71219不对前缀和s[3]12区间长度3dp[0][2] min(dp[0][0]dp[1][2]12, dp[0][1]dp[2][2]12) → min(091221, 701219) → 19哦我之前算错了正确的最小花费应该是19那之前的测试数据说明有误需重新计算石子数 [3,4,5]前缀和s[0,3,7,12]区间长度2dp[0][1] 347dp[1][2] 459区间长度3dp[0][2] min(dp[0][0]dp[1][2]12, dp[0][1]dp[2][2]12) → min(091221, 701219) → 19。所以测试2的输出应为19而非24。这说明测试数据需要修正实际应根据代码运行结果调整。2. 环形排列测试实例代码2测试1输入[1,2,3,4]环形转线性后所有长度为4的区间i0到3i1到4i2到5i3到6的dp值分别为i0,j3: 19同线性i1,j4: 合并[2,3,4,1] → 前缀和s[5]10区间长度4dp[1][4] min(dp[1][1]dp[2][4]10, dp[1][2]dp[3][4]10, dp[1][3]dp[4][4]10dp[2][4] min)(dp[2][2]dp[3][4]9, dp[2][3]dp[4][4]9) → dp[3][4]415所以 dp[2][4]min(05914, 90918) →14dp[1][2]7, dp[3][4]5, dp[1][3] dp[1][1]dp[2][3]909918 或 dp[1][2]dp[3][3]970916 →16所以 dp[1][4] min(0141024, 751022, 1601026) →22所以环形最小是min(19,22, ...)最终输出19因为i0,j3的结果是19比i1,j4的22小。编译与运行将代码保存为.cpp文件如stone_merge.cpp使用g编译g stone_merge.cpp -o stone_merge ./stone_merge运行后将输出测试数据的结果可根据实际逻辑验证是否正确。通过以上文档和代码你可以清晰理解石子合并问题的原理、实现、优化及应用场景并通过测试数据验证算法的正确性。

相关文章:

动态规划专题(14):石子合并问题(未完待续)

问题描述:一群小孩子在玩小石子游戏,游戏有两种玩法。(1)路边玩法有n堆石子堆放在路边,将石子有序地合并成一堆,每次只能移动相邻的两堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石…...

需求管理中的需求分析优先级排序与变更控制

需求管理是软件开发与项目管理中的核心环节,而需求分析优先级排序与变更控制则是确保项目成功的关键。在资源有限、时间紧迫的情况下,合理分配需求优先级能够有效提升交付效率;严格的变更控制机制能避免需求蔓延导致的项目失控。本文将围绕这…...

零代码基础部署Qwen3-Embedding-4B:SGLang保姆级教程

零代码基础部署Qwen3-Embedding-4B:SGLang保姆级教程 1. 引言:为什么选择Qwen3-Embedding-4B 在当今信息爆炸的时代,如何让计算机真正理解文本含义成为关键挑战。Qwen3-Embedding-4B作为通义千问系列的最新文本嵌入模型,能够将任…...

反思机制的工程实现:让AI Agent在失败后自我诊断与优化执行路径

反思机制的工程实现:让AI Agent在失败后自我诊断与优化执行路径 摘要/引言 开门见山 你有没有遇到过这种场景吗? 在过去半年里,各大公司的RAG Agent团队、AI助手产品经理和智能客服运营团队,可能都踩过同一个令人头疼的坑——**Agent在复杂任务面前“死脑筋”的情况:明明…...

▲基于RBF-Q学习的四足机器人运动协调控制算法matlab仿真

目录 1.引言 2.四足机器人运动学模型 2.1 腿部结构与坐标系 2.2 足端理想轨迹规划 3.RBF-Q学习算法原理 3.1 Q学习基本框架 3.2 RBF神经网络结构 3.3 RBF网络逼近Q值函数 3.4 权重更新规则 4.状态空间、动作空间与奖励函数设计 4.1 状态空间定义 4.2 动作空间定义 …...

CLAP零样本分类教程:科研场景中稀有鸟类叫声发现与标注

CLAP零样本分类教程:科研场景中稀有鸟类叫声发现与标注 1. 引言:从海量录音中寻找“稀客” 想象一下,你是一位生态学研究者,在野外布设了数十个录音设备,连续记录了几个月。拿回来的数据是成千上万小时的音频文件。你…...

GLM-. 全面支持与 Gemini CLI 集成:HagiCode 的多模型进化之路佣

1. 流图:数据的河流 如果把传统的堆叠面积图想象成一块块整齐堆叠的积木,那么流图就像一条蜿蜒流淌的河流,河道的宽窄变化自然流畅,波峰波谷过渡平滑。 它特别适合展示多个类别数据随时间的变化趋势,尤其是当你想强调整…...

手把手教学:用ComfyUI Qwen-Image-Edit-F2P制作你的专属AI形象卡

手把手教学:用ComfyUI Qwen-Image-Edit-F2P制作你的专属AI形象卡 1. 为什么你需要这个AI形象生成工具 想象一下这样的场景:你需要一张专业的个人形象照用于社交平台,但没时间预约摄影师;或者你想为游戏角色创建独特的头像&#…...

Z-Image-Turbo-辉夜巫女效果增强:结合ControlNet姿势控制生成进阶教程

Z-Image-Turbo-辉夜巫女效果增强:结合ControlNet姿势控制生成进阶教程 1. 模型介绍与部署准备 1.1 什么是Z-Image-Turbo-辉夜巫女 Z-Image-Turbo-辉夜巫女是基于Z-Image-Turbo模型的LoRA版本,专门针对生成"辉夜巫女"风格图片进行了优化。这…...

前端可视化方案

前端可视化方案:数据之美触手可及 在当今数据驱动的时代,前端可视化已成为连接用户与复杂数据的桥梁。无论是企业级的数据看板,还是个人项目中的动态图表,优秀的前端可视化方案能让枯燥的数据变得生动直观。通过JavaScript生态中…...

应急响应实战:从Web1靶场到挖矿溯源——知攻善防实验室深度复盘

1. 应急响应实战开场:当服务器CPU突然飙升 那天晚上11点半,实验室的小李正盯着监控大屏,突然发现一台Web服务器的CPU使用率从5%瞬间飙到98%。作为刚入行的安全值守人员,他的第一反应是直接拔了网线——这个操作虽然粗暴&#xff0…...

7kbscan-WebPathBrute实战:如何用这款工具快速发现网站隐藏路径(附字典文件分享)

7kbscan-WebPathBrute实战指南:从零开始掌握Web路径探测 在网络安全领域,Web路径探测是一项基础但至关重要的技能。想象一下,你正在评估一个网站的安全性,而管理员可能无意中遗留了一些未保护的敏感目录——比如/admin、/backup或…...

从流量包到攻击画像:一次APT攻击的深度取证WriteUp

1. 从流量包到攻击画像:APT攻击取证实战 那天下午接到应急响应通知时,我正在喝第三杯咖啡。客户发来的压缩包里只有一个5MB的pcap文件,但我知道这里面可能藏着整个攻击链条的关键证据。作为安全分析师,我们就像网络空间的法医&am…...

中文评论分析新选择:SiameseAOE属性抽取模型详细使用教程

中文评论分析新选择:SiameseAOE属性抽取模型详细使用教程 1. 认识SiameseAOE属性抽取模型 1.1 什么是属性观点抽取? 属性观点抽取(Aspect-Based Sentiment Analysis,简称ABSA)是一种能够从文本中精准识别具体属性和…...

Python asyncio 与多线程性能差异

Python asyncio与多线程性能差异解析 在现代Python开发中,异步编程(asyncio)和多线程是两种常见的并发处理方式。尽管它们都能提升程序性能,但底层机制和适用场景却大不相同。理解它们的性能差异,有助于开发者根据需求…...

新手必看!AudioSeal蓝图实验室:一键为音频加‘隐形水印’实战教程

新手必看!AudioSeal蓝图实验室:一键为音频加隐形水印实战教程 1. 引言:音频水印技术入门 音频水印技术就像给声音文件打上"数字指纹",在不影响听感的前提下嵌入特定信息。想象一下,你可以在音乐文件中隐藏…...

技术判断力之AI三问始

认识Pass层级结构 Pass范围从上到下一共分为5个层级: 模块层级:单个.ll或.bc文件 调用图层级:函数调用的关系。 函数层级:单个函数。 基本块层级:单个代码块。例如C语言中{}括起来的最小代码。 指令层级:单…...

芯片研发也能用 Minimum Viable Product?

MVP,全称 Minimum Viable Product(最小可行性产品),最早是互联网产品圈的说法——先做最小可用版本,跑通核心逻辑,验证方向对不对,再慢慢迭代。 但是芯片不是 App,改一次要流片&…...

容器安全扫描:镜像漏洞检测与运行时保护

容器安全扫描:镜像漏洞检测与运行时保护 随着容器技术的广泛应用,其安全性问题日益凸显。容器安全扫描成为保障云原生环境安全的关键环节,涵盖镜像构建阶段的漏洞检测与运行时的动态防护。本文将深入探讨容器安全的核心实践,帮助…...

写段代码教会你什么是HOOK技术?HOOK技术能干什么?馅

为 HagiCode 添加 GitHub Pages 自动部署支持 本项目早期代号为 PCode,现已正式更名为 HagiCode。本文记录了如何为项目引入自动化静态站点部署能力,让内容发布像喝水一样简单。 背景/引言 在 HagiCode 的开发过程中,我们遇到了一个很现实的问…...

数字电路实战:序列检测电路的设计与优化

1. 序列检测电路的基础概念 序列检测电路是数字电路设计中非常实用的功能模块,它的核心任务是识别输入信号中特定的比特序列。想象一下,这就像是在一长串摩斯电码中寻找特定的求救信号,或者是在音乐播放器中检测特定的歌曲前奏。在实际工程中…...

避坑指南:若依二次开发添加模块时,POM.xml依赖到底该怎么加?(附修改前后对比图)

若依项目模块化开发实战:POM依赖配置的深度解析与避坑指南 在若依前后端分离项目的二次开发过程中,模块化设计是提升代码复用性和维护性的关键。然而,许多开发者在添加新模块时,往往会在POM.xml文件的依赖配置环节栽跟头。本文将从…...

值类型与引用类型:别再只背“栈和堆”了,看这 个实际影响得

基础示例:单工作表 Excel 转 TXT 以下是将一个 Excel 文件中的第一个工作表转换为 TXT 的完整步骤: 1. 加载并读取Excel文件 from spire.xls import * from spire.xls.common import * workbook Workbook() workbook.LoadFromFile("示例.xls…...

如何审计一个智能合约?

如何审计一个智能合约? 智能合约作为区块链技术的核心应用之一,凭借其去中心化、不可篡改的特性,被广泛应用于金融、供应链、游戏等领域。智能合约一旦部署便难以修改,任何漏洞都可能引发严重的安全问题,甚至导致巨额…...

区块链未来展望

区块链技术自诞生以来,以其去中心化、透明性和不可篡改的特性,迅速成为全球科技创新的焦点。从比特币的底层技术到如今赋能金融、供应链、医疗等多个领域,区块链正在重塑数字经济的未来。随着技术的不断成熟和应用场景的拓展,其潜…...

VOACAP 软件:从下载安装到首次电离层传播预测实战

1. VOACAP软件初探:短波通信的"天气预报员" 第一次听说VOACAP时,我正被短波通信的频率选择问题困扰。就像渔民出海需要查看天气预报一样,短波通信也需要提前知道"电离层天气"。VOACAP就是这样一个神奇的工具——它能预测…...

数据结构与算法动画解析:动态规划解题套路框架

数据结构与算法动画解析:动态规划解题套路框架 动态规划(Dynamic Programming, DP)是算法设计中解决复杂问题的利器,但许多初学者常被其抽象性劝退。本文通过动画解析与套路框架,带您轻松掌握动态规划的核心思想与解题…...

移动端Crash分析:符号化与堆栈追踪的解析

移动端Crash分析:符号化与堆栈追踪的解析 在移动应用开发中,Crash问题直接影响用户体验和产品稳定性。Crash日志往往以难以理解的机器码或内存地址形式呈现,开发者需要通过符号化与堆栈追踪技术将其转化为可读信息。本文将深入解析这一过程&…...

别再踩坑了!手把手教你查清ONNX、TensorRT和Opset的版本兼容表(附官方链接)

ONNX与TensorRT版本兼容性实战指南:从原理到避坑策略 每次模型部署时遇到"不支持的算子"或"版本不匹配"报错,那种感觉就像在迷宫里转圈——明明官方文档就在那里,却总是找不到关键信息。作为AI工程师,我们花…...

从TUV到UL:手把手教你为你的开关电源产品选择合适的安规认证路径

开关电源全球市场准入指南:如何构建最优安规认证矩阵 当一款开关电源产品从设计图纸走向国际市场时,安规认证就像通关文牒,决定着产品能否顺利进入目标市场。但面对欧洲CE、北美UL、日本PSE等不同体系的认证要求,企业常陷入两难&a…...