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

区间dp复盘

P1880 [NOI1995] 石子合并 题解复盘

模块:区间动态规划(Interval DP)

类型:区间DP + 前缀和 + 环形DP

目标:求石子合并的最小总代价和最大总代价。


基本信息

项目内容
题目编号、来源P1880 NOI1995
训练层级B
知识版块区间DP、前缀和、环形DP

解题前・关键信号识别

维度分析
目标、约束、底层结构每次只能合并相邻两堆石子,最终合并成一堆,求总代价最小值和最大值。
数据规模n≤100,O(n³) 可以通过。
候选算法和依据每次最后一定是把左右两个区间合并,因此使用区间DP。由于是环形,需要断环成链。
复杂度预判时间复杂度:O(n³)
空间复杂度:O(n²)

解题后・外化复盘

维度内容
状态定义mn[l][r]:区间[l,r]合并成一堆的最小总代价。

mx[l][r]:区间[l,r]合并成一堆的最大总代价。
状态转移枚举最后一次切分点k

mn[l][r]=min(mn[l][k]+mn[k+1][r]+sum(l,r))

mx[l][r]=max(mx[l][k]+mx[k+1][r]+sum(l,r))
遍历顺序按区间长度递增:

① 枚举区间长度 len
② 枚举左端点 l
③ 推出右端点 r
④ 枚举切分点 k
实现结构 / 核心思路① 将数组复制一遍,把环变成链。
② 求前缀和。
③ 区间DP计算所有长度≤n的区间。
④ 枚举所有长度为 n 的区间,更新答案。
错因回溯① 转移时写成了dp[k][r],导致第 k 堆重复计算。正确应为dp[k+1][r]

② 忘记复制数组,无法处理环。

③ 忘记初始化mn[][]=INF
边界和易错点1.dp[i][i]=0,一堆石子不用合并。

2. 求最小值初始化 INF。

3. 求最大值初始化 0。

4. 环必须复制数组。

5. 最后答案只统计长度为 n 的区间。
下次看到什么信号,我应该想到这个方法看到:

① 求一段区间最优值。
② 最后一步可以拆成左右两个区间。
③ 每次操作只能发生在区间内部。

想到:区间DP。

为什么要复制数组?

原数组:

4 5 9 4

复制:

4 5 9 4 4 5 9 4

例如:

从第二堆开始断开:

5 9 4 4

对应:

区间 [2,5]

因此:

所有长度为 n 的区间:

[1,n] [2,n+1] ... [n,2n-1]

刚好对应所有断环方式。


为什么要加 sum(l,r)?

例如:

区间:

4 5 9

最后一次一定会变成:

(4 5) + (9)

或者:

(4) + (5 9)

左右两边已经分别合并完成。

最后:

左右两堆还要再合并一次。

最后一次合并后的石子数:

4+5+9=18

因此:

最后一次代价就是:

sum(l,r)

所以状态转移必须写成:

dp[l][k]+dp[k+1][r]+sum(l,r)

为什么枚举区间长度?

因为:

dp[1][4]

依赖:

dp[1][2] dp[3][4] dp[1][3] dp[2][4]

这些都是更短的区间。

因此必须:

长度2 ↓ 长度3 ↓ 长度4 ↓ …… ↓ 长度n

模板:

for(intlen=2;len<=n;len++){for(intl=1;l+len-1<=2*n;l++){intr=l+len-1;for(intk=l;k<r;k++){}}}

AC完整代码

#include<iostream>#include<algorithm>usingnamespacestd;constintN=205;constintINF=1e9;inta[N];ints[N];intmn[N][N];intmx[N][N];intmain(){intn;cin>>n;for(inti=1;i<=n;i++){cin>>a[i];a[i+n]=a[i];}for(inti=1;i<=2*n;i++){s[i]=s[i-1]+a[i];}for(inti=1;i<=2*n;i++){for(intj=1;j<=2*n;j++){mn[i][j]=INF;mx[i][j]=0;}mn[i][i]=0;mx[i][i]=0;}for(intlen=2;len<=n;len++){for(intl=1;l+len-1<=2*n;l++){intr=l+len-1;for(intk=l;k<r;k++){intsum=s[r]-s[l-1];mn[l][r]=min(mn[l][r],mn[l][k]+mn[k+1][r]+sum);mx[l][r]=max(mx[l][r],mx[l][k]+mx[k+1][r]+sum);}}}intansMin=INF;intansMax=0;for(intl=1;l<=n;l++){intr=l+n-1;ansMin=min(ansMin,mn[l][r]);ansMax=max(ansMax,mx[l][r]);}cout<<ansMin<<endl;cout<<ansMax<<endl;return0;}

模型总结

模型状态转移
路径DPdp[i][j]从相邻位置转移
背包DPdp[j]从容量转移
LISdp[i]从前面位置转移
区间DPdp[l][r]从左右区间转移

区间DP统一模板

for(len=2;len<=n;len++){for(l=1;l+len-1<=n;l++){r=l+len-1;dp[l][r]=初始化;for(k=l;k<r;k++){dp[l][r]=最优(dp[l][k],dp[k+1][r]);}}}

记忆

看到: ① 求一个区间最优值 ② 最后一步可以拆成左右两部分 ③ 枚举切分位置 ↓ 想到: 区间DP 状态: dp[l][r] ↓ 枚举长度 ↓ 枚举左端点 ↓ 枚举切分点 ↓ dp[l][k]+dp[k+1][r] ↓ 如果最后还需要一次操作 + 区间贡献(如sum(l,r))

P3146 248 题解复盘

模块:动态规划(Dynamic Programming)

类型:区间DP

目标:通过不断合并相邻且相等的数字,使最终数字最大。


基本信息

项目内容
题目编号、来源P3146 USACO 2016 Open Gold
训练层级普及+/提高−
知识版块区间DP

解题前・关键信号识别

维度分析
目标、约束、底层结构每次只能合并相邻且相等的数字,最后求能够得到的最大数字。
数据规模n≤248,典型区间DP规模。
候选算法和依据一个区间能否合并只与更短的两个子区间有关,因此采用区间DP。
复杂度预判时间复杂度:O(n³)
空间复杂度:O(n²)

解题后・外化复盘

维度内容
状态定义dp[l][r]:表示区间[l,r]如果能够全部合并成一个数字,那么这个数字是多少;不能合并则为 0。
状态转移枚举最后一次合并的位置k

若:
dp[l][k]==dp[k+1][r]且都不为0。

则:
dp[l][r]=max(dp[l][r],dp[l][k]+1)
遍历顺序按区间长度从小到大枚举。因为长区间依赖短区间。
实现结构 / 核心思路① 初始化所有长度为1的区间。
② 枚举区间长度。
③ 枚举左端点。
④ 枚举分割点。
⑤ 判断左右是否能合并且数值相同。
错因回溯① 容易误认为和石子合并一样需要前缀和。

② 容易忘记判断左右区间是否能够合并(即dp!=0)。
边界和易错点1. 初始化:dp[i][i]=a[i]

2. 左右区间必须都能合并。

3. 左右区间最终数字必须相同才能继续合并。
下次看到什么信号,我应该想到这个方法看到:

① 连续区间。
② 每次只能合并相邻区间。
③ 最后一定由左右两个子区间组成。

想到:区间DP。

为什么这样定义状态?

例如:

1 1 2

区间:

[1,2]

可以:

1 1 ↓ 2

因此:

dp[1][2]=2;

如果:

1 2

不能合并。

则:

dp[1][2]=0;

表示:

这个区间无法最终变成一个数字。

为什么这样转移?

设:

[l......k][k+1......r]

最后一次操作一定是:

左区间 + 右区间

因此:

必须:

左区间已经合并完成 右区间已经合并完成

并且:

最终数字相同

例如:

2 2

才能:

2 2 ↓ 3

因此:

if(dp[l][k]!=0&&dp[l][k]==dp[k+1][r]){dp[l][r]=max(dp[l][r],dp[l][k]+1);}

为什么按区间长度枚举?

因为:

dp[l][r]

依赖:

dp[l][k] dp[k+1][r]

而:

这两个区间一定更短。

所以:

必须:

长度1 ↓ 长度2 ↓ 长度3 ↓ …… ↓ 长度n

AC完整代码

#include<iostream>#include<algorithm>usingnamespacestd;inta[255];intdp[255][255];intmain(){intn;cin>>n;intans=0;for(inti=1;i<=n;i++){cin>>a[i];dp[i][i]=a[i];ans=max(ans,a[i]);}for(intlen=2;len<=n;len++){for(intl=1;l+len-1<=n;l++){intr=l+len-1;for(intk=l;k<r;k++){if(dp[l][k]!=0&&dp[l][k]==dp[k+1][r]){dp[l][r]=max(dp[l][r],dp[l][k]+1);}}ans=max(ans,dp[l][r]);}}cout<<ans;return0;}

模型总结

模型状态转移
石子合并dp[l][r]最小/最大代价min/max + sum
248dp[l][r]最终数字左右相等时+1

与石子合并区别

石子合并248
求总代价求最终数字
需要前缀和不需要前缀和
左右区间一定能合并左右必须最终数字相同
转移:+sum转移:+1

记忆

看到: ① 相邻区间合并 ② 一个区间最终变成一个数字 ③ 左右区间共同决定答案 ↓ 想到: 区间DP ↓ dp[l][r] 表示: 区间最终能合成的数字 ↓ 枚举分割点 k ↓ 左右相等 ↓ +1
http://www.jsqmd.com/news/1401210/

相关文章:

  • 从一次慢 OData 请求追到 ABAP Data Provider,深入理解 SAP Gateway Performance Trace
  • OpenClaw离线安装包部署详细教程,TopClaw免网络也能三步完成中文版
  • 2024年C语言自学指南:从核心概念到项目实战的完整路径
  • 【电动车托运怎么寄才不踩坑?2026年寄车省钱攻略+避坑指南】 - 快递物流资讯
  • 通化除甲醛公司甲醛治理公司剖析:金耀环境除甲醛 - CMA甲醛检测中心
  • 随笔19
  • 2026年常熟一般纳税人代账推荐:进销项、库存与月末差异核对指南 - 企业服务研究所
  • GPT-Image 2提示词逆向工程:329条高质量模板解析与应用指南
  • OpenClaw Skills生态实战:10大必装插件与自动化工作流搭建指南
  • OMO 同步课如何落地?从课堂连接到教学交付闭环
  • lambda表达式的使用(3)
  • 合肥除甲醛公司甲醛治理公司剖析:金耀环境除甲醛 - CMA甲醛检测中心
  • 2026年厦门SEO/GEO服务实力盘点:正规服务商甄选指南与避坑技巧附优质公司推荐 - 行业观察网
  • 铁岭除甲醛公司甲醛治理公司剖析:金耀环境除甲醛 - CMA甲醛检测中心
  • 林伽一 · AI科技日报 | 2026年08月14日
  • 影之刃零发售日介绍 影之刃零什么时候发售
  • GitHub CodeQL 成本全解析:每月1000美元背后的隐藏费用与效能权衡
  • MySQL到金仓:自增主键与序列改造全流程——AUTO_INCREMENT兼容、并发写入与回退实战
  • 2026年常熟小规模企业代账推荐:开票频次、工资社保与资料交接指南 - 企业服务研究所
  • Simulink仿真单相方波逆变电路:从全桥拓扑到波形分析实战
  • memcpy 函数的底层原理详解(结合C++代码分析)
  • 汉中除甲醛公司甲醛治理公司剖析:金耀环境除甲醛 - CMA甲醛检测中心
  • 2026年AI推广SEO/GEO服务商选型指南:多家正规实力平台盘点+合作避坑攻略 - U渠道
  • 黄石除甲醛公司甲醛治理公司剖析:金耀环境除甲醛 - CMA甲醛检测中心
  • 数据采集卡从入门到精通(1):什么是数据采集卡——物理世界与数字世界之间的翻译官
  • 2026宁波工业装备外贸GEO优化公司盘点推荐7家:靠谱服务商甄选指南+合作避坑FAQ - 商业大观
  • GLM ZCode AI编程助手实战:从安装配置到项目开发全指南
  • 开关电源设计实战:从CCM/DCM模式到EMI整改全解析
  • 南通除甲醛公司甲醛治理公司剖析:金耀环境除甲醛 - CMA甲醛检测中心
  • HarmonyOS 7.0 端侧重建任务排队:3DGS 计算和 UI 交互怎么互不拖累