区间DP
· 阅读需 3 分钟
掰开!!!--洞洞拐
(宇宙免责声明:因为本人在网校之前并未跟随老师系统的学过(zy背锅),又加之上课时肚子疼没怎么听,导致这篇文章完全是我刚刚(2025·8·9 16:30~16:55)拉💩时自己思考对区间的理解,若有错误,欢迎指正)
区间是基于分治思想(应该是吧)求解问题的一种动态规划模型,它的做法十分通俗易懂,即用小区间合并成大区间最后得到整个区间的最优解
区间一般有很明显的标志->相邻两数合并,这也是区间的核心操作,通过求解每个子段的最优解并拼接得到新的最优解
我在这说也没用,上例题:石子合并,看下代码
//f[i][j]代表把i->j合并所用的最小花费
for( int i=1;i<=n;i++ ) f[i][i]=0;//初始化:将i->i合并不用花费
for( int len=2;len<=n;len++ )//枚举区间长度
for( int l=1, r=l+len-1;r<=n;l++, r++ )//枚举区间
for( int k=l;k<r;k++ )//枚举中间的断点->合并时左区间的右端点
f[l][r]=min( f[l][r],f[l][k]+f[k+1][r]+pre_sum[r]-pre_sum[l-1] );//转移
看了代码是不是豁然开朗?区间的式子基本如此:
写成代数式为(value代表合并的花费),大多区间的转移方程都与它相似甚至相同
但在部分情况,我们需要对数组进行一些修改后再进行,例如能量项链
这时候,我们要进行的部分是一个环,不好直接在原数组上进行,所以我们要断环成链,将原数组复制一份放在后面
这样处理后,数组中任一长度为的子段都是原环的一种可能,代码如下
for( int i=1;i<=n;i++ ) cin>>power[i], power[i+n]=power[i];//断环成链
for( int len=2;len<=n;len++ )
for( int l=1, r=l+len-1;r<=2*n;l++, r++ )
for( int k=l;k<r;k++ )
f[l][r]=max( f[l][r],f[l][k]+f[k+1][r]+power[l]*power[k+1]*power[r+1] );
int ans=0;
for( int i=1;i<=n;i++ ) ans=max( ans,f[i][i+n-1] );//答案是n个断出的长度为n的子段中的最大值
断环成链是区间中一个十分重要的技巧,它可以将不好处理的环上问题转化为线性的问题,是处理区间问题中经常需要考虑的点(出题人也喜欢考)
