3.16记录 分段DP

”知识是第一位的。“

(原题P1388)
我没有自己实现一遍这道题的完整情况,因为这题他是真的有bug,我们研究到8:30我才刚刚能看得懂题解到底说了啥(感谢学哥)。关键还是在于理解这种处理的思路。
首先正常来讲此题是一个分段DP,我们暂且不考虑0可能带来的影响。
根据(a+b)* c >= a+b*c <=> c >= 1,没有0的时候整个序列都是先把这串数合成(k+1)个段然后再相乘。说白了,能乘则乘。
那么思路就出来了:设前i个数用j个乘号得出的最大值为dp[i][j],每一个位置,我乘就是更新,不乘与原来的情况无异,那么显然有dp[i][j] = max(dp[i][j] , dp[k][j - 1] + sum[k] – sum[i])。
用这个递推式就完全足以应付不考虑0的情况了。我们只需要在sum数组里面存一遍前缀和就可以了。
注意:分段DP不是区间DP,没必要枚举把k个乘号一下子分给两边的情况,其实完全就是一般的DP,我们甚至可以说这玩意儿跟01背包就是一个原理。

接下来讨论一下0的问题。
0的问题,实质上就是打括号的问题。
经过了一大顿探讨,我们没有得出任何一个靠谱的特判,上一回hanoi那种两种模式并行的方式也不靠谱了,毕竟我们可以一会儿打括号一会儿不打括号。综上所述,我们只能像暴力一样考虑问题,设dp[i][j][k]表示从i到j分了k块得出的最大值, 依次枚举以下项目:括号数、区间长、左端点、分区间的位置、左区间用掉的括号数。
for(p=1;p<=m;p++) //枚举区间内的括号块的个数
for(r=p+1;r<=n;r++) //枚举区间的宽度
for(i=1;i+r-1<=n;i++){ //枚举左端点
j=i+r-1; //求出右端点
for(k=i;k<j;k++) //枚举区间dp中间点
for(q=0;q<k-i+1&&q<p&&p-q-1<j-k;q++){//枚举左区间括号块的个数
f[i][j][p]=max(f[i][j][p],f[i][k][q]*f[k+1][j][p-q-1]);
if(p-q<j-k)
f[i][j][p]=max(f[i][j][p],f[i][k][q]+f[k+1][j][p-q]);
}
}

(这段是从题解里面掏出来的。
题解地址https://www.luogu.com.cn/problem/solution/P1388)
这个和我们刚才写的那一种其实大同小异,i,j的处理压根没区别,就是这个括号数有所变化。这里我们认为,乘号必定消耗括号,毕竟这会使一个式子一分为二;这样一来,我们相当于模拟了所有可能取最大,完全去掉了不考虑0的时候部分“贪心“的处理。这是一个O(n5)的算法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值