《C++算法竞赛攻略:从入门到降维打击》——第14章

好的,这是根据您提供的目录大纲和要求为您撰写的 《C++算法竞赛攻略:从入门到降维打击》第14章:区间DP、树形DP与其他 的完整内容。


第14章:区间DP、树形DP与其他

欢迎来到动态规划的进阶领域。在前两章,我们已经掌握了线性DP和背包DP,它们通常处理序列或集合问题。本章将介绍三种更特殊的DP模型,它们建立在特定的结构之上:区间DP处理“合并”与“分割”问题,树形DP在树状结构上进行状态转移,而状压DP则利用二进制位运算巧妙地处理小规模集合状态。掌握它们,你将能攻克更多结构性强的复杂问题。

14.1 区间DP模型与实现

1. 核心思想

区间DP,顾名思义,是在一个“区间”上进行动态规划。它通常用于解决一类可以被分解为子区间,并且子区间的解能够合并为父区间解的问题。

其鲜明特征是:

  • 合并/分割:问题最终要求解整个序列(大区间)上的最优值,而这个最优值来自于其所有子区间的某个最优组合。例如,石子合并问题、括号匹配问题、矩阵链乘问题等。
  • 状态表示:通常使用二维数组 dp[i][j]dp[i][j]dp[i][j] 表示区间 [i,j][i, j][i,j] 上的最优值。
  • 递推顺序:DP的计算顺序不再是简单的从左到右。而是依赖于区间长度。我们总是先计算出所有长度为1的区间的值,然后是长度为2的,接着是长度为3的…直到最后计算出我们想要的、覆盖整个序列的区间 [1,N][1, N][1,N] 的值。

2. 区间DP解题模板

区间DP的实现代码具有高度的模式化,其骨架如下:

  1. 初始化 (Initialization):初始化长度为1的区间,即 dp[i][i]dp[i][i]dp[i][i] 的值。
  2. 枚举区间长度 (Enumerate Length):外层循环从 len=2len = 2len=2NNN
  3. 枚举区间起点 (Enumerate Start):中层循环枚举区间的左端点 iii,则右端点 j=i+len−1j = i + len - 1j=i+len1
  4. 枚举分割点 (Enumerate Split Point):内层循环在区间 [i,j][i, j][i,j] 中枚举分割点 kkk (KaTeX parse error: Undefined control sequence: \< at position 9: i \le k \̲<̲ j) 。
  5. 状态转移 (State Transition):根据分割点 kkk 将区间 [i,j][i, j][i,j] 分为 [i,k][i, k][i,k][k+1,j][k+1, j][k+1,j] 两部分,进行状态转移。

状态转移方程的一般形式为:
dp[i][j]=min⁡i≤k<j/max⁡i≤k<j{ dp[i][k]+dp[k+1][j]+cost(i,j)}dp[i][j] = \min_{i \le k < j} / \max_{i \le k < j} \{ dp[i][k] + dp[k+1][j] + \text{cost}(i, j) \}dp[i][j]=ik<jmin/ik<jmax{ dp[i][k]+dp[k+1][j]+cost(i,j)}
其中 cost(i,j)\text{cost}(i, j)cost(i,j) 表示合并两个子区间的代价。

3. C++ 实现与经典例题:石子合并

问题描述:在一个圆形操场的四周摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。求将所有石子合并成一堆的最小得分。

分析:这是一个典型的区间DP问题。我们用 dp[i][j]dp[i][j]dp[i][j] 表示合并区间 [i,j][i, j][i,j] 的石子堆所需的最小代价。为了合并区间 [i,j][i, j][i,j],我们必然是先将某个子区间 [i,k][i, k][i,k] 合并成一堆,再将子区间 [k+1,j][k+1, j][k+1,j] 合并成一堆,最后将这两堆合并。

  • 状态表示dp[i][j]dp[i][j]dp[i][j] 表示合并第 iii 堆到第 jjj 堆石子的最小代价。
  • 状态计算dp[i][j]=min⁡i≤k<jdp[i][k]+dp[k+1][j]+∑l=ijstones[l]dp[i][j] = \min_{i \le k < j} { dp[i][k] + dp[k+1][j] + \sum_{l=i}^{j} \text{stones}[l] }dp[i][j]=minik<jdp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱看烟花的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值