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

题解 P1295 [TJOI2011] 书架

传送门

题意

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

分析

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

切分($n\le10^3$)

写这种题毕竟要从暴力打起,我们用 $n^2$ 解决这个 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(n^2)$。

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

正解

我们显然是要优化内层的循环。
动态规划的一个重点就是继承,
令当前需要处理的节点为 $i$, 我们令 $\mathit{g}_{i,j} =f_{j-1}+ \max_{k=j}^{k \le i}a_k$,
显然 $f_i=\max_{j=1}^{j\le i} \mathit{g}_{i,j} (qzh_i-qzh_j\le m )$。

接下来就是解决在 $\mathit{g}_{i,j}$ 的转移,对于其转移,只会修改后面的 $\max_{k=j}^{k \le i}a_k$,多了一个 $a_i$,那么假如说 $a_i$ 小于原本的,那么 $\mathit{g}_{i,j}$ 就不变。
我们仔细观察 $\max_{k=j}^{k \le i}a_k$ 的变化,我们可以发现,我们的 $\max_{k=j}^{k \le i}a_k$ 是单调不增的,是如同阶梯状的。
这也就表示,我们的 $\mathit{g}_{i,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(n\log n)$。

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

posted @ 2023-08-01 21:50  djh0314  阅读(30)  评论(0)    收藏  举报  来源
浏览器标题切换
浏览器标题切换end