一类特殊的区间 dp
前言
这东西去年就看了,但是感觉理解的不是很深刻。今年再来加深一下理解吧!虽然可能这辈子不会再用到了。
引入
先看一个题吧:CF1132F
没什么分析的必要,直接给个转移方程吧。
没啥难理解的。不过现在让我们深度(能有多深?)思考一下。这个题之所以可以有第二条转移,恒为 \(1\) 的代价是关键的,它允许我们每次可以不考虑相同的两端点的其中一个,让这个贡献只在另一个端点处计算就好了。那么我们加强一下,代价与每次删除的长度呈函数关系。这样一来,第二条转移就不成立了。因为要计算贡献,仅仅知道“有”是不够的,还得知道“有多长”。这引出了今天的主角。
e.g.UVA10559 Blocks
这位更是重量级
代价变为了 \(x^2\),上面两维的状态无法很好地解决问题了。我们应该需要一个记录长度的第三维。
那么究竟要怎么设计呢?不妨从整个过程来看看吧!现在有一段连续的由同样字母组成的子串,我们可以进行两种操作:
- 删除,并把左右合并。
- 保留,等待其他子串与之合并。
然后我发现并不能很自然地推出状态。究竟是注意力不够还是怎么样,我不懂。总之就直接给出吧:
设 \(f_{i,j,k}\) 表示删除了 \(i\sim j\) 的同时,还删除了区间后方(不包括 \(s_j\))的 \(k\) 个与 \(s_j\) 相同的字符,所能获得的最大价值。(当然了,定义为区间前方也是一样的)
有必要提及的一点是,这里的删除是和 \(s_j\) 一起删掉,也就是中间的空缺在这之前就被删掉了。换句话说,这 \(k\) 个字符可能原本并不是连续的一段。
然后依照上面的操作,我们有两种转移。
第一种:
含义是:我们在 \(i\sim j\) 中找到一个 \(s_p=s_j\),把 \(p+1\sim j-1\) 删去,然后合并剩余部分。
第二种:
含义是:我们直接删掉 \(s_j\),合并剩余部分。
答案就是 \(f_{1,n,0}\)。
这两个转移是互相独立的。他们又相辅相成,涵盖了所有情况,还可以保证状态中的 \(k\) 个字符间的多余字符一定先被处理掉了。
这个状态设计真的很妙。妙在我至今不知道为什么要这么设计,原因何在?而这两个转移之间又是如何做到不重不漏的?我想,我只能感性理解了。可能这就是我缺少的“灵感”吧。不过记住了总是好的。但是只是记住了,什么时候能做到灵活运用呢?
不说闲话了。个人感觉记搜好写点?可能还能规避掉一些无用的状态?总之代码如下:
int solve(int l,int r,int k){ if(l>r) return 0; if(l==r) return sqr(k+1); if(f[l][r][k]>=0) return f[l][r][k]; int res=0; res=max(res,solve(l,r-1,0)+sqr(k+1)); FOR(i,l,r-1){ if(a[i]==a[r]){ res=max(res,solve(i+1,r-1,0)+solve(l,i,k+1)); } } f[l][r][k]=res; return res; }
时间复杂度应该是 \(\mathcal{O}(n^4)\),不过我认为跑不满。
更多题目
P2135 这个完全一样吧,只有输入需要简单处理一下。
CF1107E 只是代价计算方式改变了而已。
这个题还是有点厉害的。
首先大体框架还是和上面一样。只不过消除掉一段并没有贡献了,而且在第二种转移时,如果第三维不足 \(k-1\) 个,则我们可以给第三维 \(+1\),并且消耗 \(1\) 的代价。如果正好有 \(k-1\) 个,则可以直接删去。不可能多于 \(k-1\) 个——我们要通过第一种转移时对 \(k-1\) 取 \(\min\) 来保证这个。
int dp(int l,int r,int sum){ if(l>r) return 0; if(f[l][r][sum]!=-1) return f[l][r][sum]; f[l][r][sum]=INF; int ans=INF; if(sum<k-1) ans=min(ans,dp(l,r,sum+1)+1); else if(sum==k-1) ans=dp(l+1,r,0); FOR(i,l+1,r) if(a[i]==a[l]) ans=min(ans,dp(l+1,i-1,0)+dp(i,r,min(k-1,sum+1))); f[l][r][sum]=ans; return f[l][r][sum]; }
(这里的 \(sum\) 指的是区间前的 \(sum\) 个)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现