决策单调性 dp

决策单调性的一些优化技巧

  • 对于判断决是否满足决策单调性,可以打个暴力验证一下

相邻层之间转移 - 分治

  • 适用于形如 \(O(nk)\) 的二维 dp,转移为上层转移到下层,不在同一层转移,并且具有决策单调性
  • 因为决策具有单调性,所以考虑分治,先求出 mid 的决策点,那么 \(1\leq i<mid\) 的决策点为 \([1,P]\),对于 \(mid<i\leq n\) 的决策点为 \([P,n]\),递归下去,每一层的复杂度就是 \(O(n\log n)\)

LG P4072 [SDOI2016]征途

给定一个序列,希望分成 \(m\) 段,使得每一段的和平方的和最小

  • 显然设 \(dp_{i,j}\) 表示前 \(i\) 个数分成 \(j\) 段的最小值,然后分治就可以了
inline void solve(int l,int r,int L,int R,int j) {
    if(l>r) return ;
    int mid=l+r>>1,p=0;
    FOR(i,L,min(R,mid-1)) {
        if(f[i][j-1]+(sum[mid]-sum[i])*(sum[mid]-sum[i])<f[mid][j]) p=i; 
        f[mid][j]=min(f[mid][j],f[i][j-1]+(sum[mid]-sum[i])*(sum[mid]-sum[i]));
    }
    solve(l,mid-1,L,p,j);solve(mid+1,r,p,R,j);
}
signed main() {
    n=read();m=read();
    FOR(i,1,n) FOR(j,1,m) f[i][j]=1e16;
    sum[0]=0;
    FOR(i,1,n) a[i]=read(),sum[i]=sum[i-1]+a[i],f[i][1]=sum[i]*sum[i];
    FOR(i,2,m) solve(1,n,1,n,i);   
    cout<<(m*f[n][m]-sum[n]*sum[n])<<endl;
    return 0;
}
  • 有一类题目的直接贡献比较难算,但是仍然可以考虑暴力做法,因为分治树的结构是非常特殊的

CF833B The Bakery

  • 还是考虑分治计算,区间数颜色直接用莫队来做,看起来很暴力,但是实际上每一层的复杂度仍然是 \(O(n\log n)\)

二分队列

  • 方程类似于 \(f_i=\min_{j=0}^{i-1} f_j+w(j,i)\)
  • 考虑用一个单调队列维护目前的连续的最优决策点
  • 因为满足决策单调性,所以对于相邻的两个决策点一定是 \(i\) 对于左区间更优,\(i+1\) 对于右区间更优,设这个分割点为 \(k_{i,i+1}\),那么队列中的值一定要满足 \(k_{1,2}\leq k_{2,3},...\)
  • 然后每次转移的时候将开头对于当前位置已经不够优的决策点弹走,可以通过二分快速判断,然后队头就是对于当前位置的最优决策点,然后将当前位置压入队尾并弹出不够优秀的尾部决策点
posted @ 2022-07-26 21:02  Kzos_017  阅读(86)  评论(0编辑  收藏  举报