把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

题解 P1295 [TJOI2011] 书架

传送门

题意

给出一个长度为 n 的序列 h,请将 h 分成若干段,满足每段数字之和都不超过 m,最小化每段的最大值之和。 (~实际上就是简化题意~)。

分析

这是一个区间划分问题,自然可以想到使用 DP 解决此题。

切分(n103

写这种题毕竟要从暴力打起,我们用 n2 解决这个 30 分。

f[0]=0;
for(int i=1; i<=n; ++i) {
int mx=0;
f[i]=INF;
for(int j=i; j; --j) {
if(qzh[i]-qzh[j-1]>m) break;
mx=max(mx,a[j]);
f[i]=min(f[i],f[j-1]+mx);
}
}

时间复杂度:O(n2)

虽然切的是 30 分,但是却可以拿到 56 分,在开了 O2 后还可以拿到 77 分的高分,(如果是比赛已经可以放弃了)。

正解

我们显然是要优化内层的循环。
动态规划的一个重点就是继承,
令当前需要处理的节点为 i, 我们令 gi,j=fj1+maxk=jkiak
显然 fi=maxj=1jigi,j(qzhiqzhjm)

接下来就是解决在 gi,j 的转移,对于其转移,只会修改后面的 maxk=jkiak,多了一个 ai,那么假如说 ai 小于原本的,那么 gi,j 就不变。
我们仔细观察 maxk=jkiak 的变化,我们可以发现,我们的 maxk=jkiak 是单调不增的,是如同阶梯状的。
这也就表示,我们的 gi,j 是可以用区间修改的。

归纳一下,我们想要实现:区间的查询维护答案,区间修改维护 g 数组。显然啊,我们用线段树来优化我们的 DP。

为了维护最大值的变化,我们使用一个单调递减的单调栈,这样我们可以实现阶梯型的修改了。

tree.build(1,1,n+1);
f[0]=0;
tree.change(1,1,n+1,1,a[1]);
int it=1;
for(int i=1; i<=n; ++i) {
while(top&&a[st[top]]<=a[i]) {
tree.change(1,1,n+1,st[top-1]+1,st[top],a[i]-a[st[top]]);
--top;
}
while(it<=i&&qzh[i]-qzh[it-1]>m) ++it;
st[++top]=i;
f[i]=tree.query(1,1,n+1,it,i);
tree.change(1,1,n+1,i+1,f[i]+a[i+1]);
}
cout<<f[n];

时间复杂度:O(nlogn)

总结一下,我们这道题考验了我们动态规划的优化,与单调栈维护最值的作用。

posted @   djh0314  阅读(14)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示