单调队列优化DP

步骤

  • 加入所需元素:向单调队列重复加入元素直到当前元素达到所求区间的右边界,这样就能保证所需元素都在单调队列中.
  • 弹出越界队首:单调队列本质上是维护的是所有已插入元素的最值,但我们想要的往往是一个区间最值.于是我们弹出在左边界外的元素,以保证单调队列中的元素都在所求区间中.
  • 获取最值:直接取队首作为答案即可.

单调队列优化多重背包

问题

有 \(k\) 种物品,每种价值为 \(v_i\),重量为 \(w_i\),个数为 \(x_i\);总容量为 \(W\),求最大价值

解析

容易得到转移方程 \(f_{ij}={max} ^{k_i}_{k=0} f_{i-1,j-k*w_i}+k*v_i \space (j-k+w_i\ge0)\)

但这样不方便转移,因此我们可以将方程转变为

\[f_{i,j+k*w_i}={max}^{k}_{p=0} f_{i-1,j+p*w_i}+p*v_i-k*v_i \space(j+k*w_i\le W,k-p\le k_i) \]

这样就可以用单调队列维护一个长度为 \(k_i\),元素大小为 \(f_{i-1,j+p*w_i}+p*v_i\) 的滑动窗口

 for (int i = 1; i <= n; i++) {
    for (int y = 0; y < w[i]; y++) {
      // 清空队列
      deque<int>().swap(q);
      for (int x = 0; x * w[i] + y <= W; x++) {
        // 弹出不在范围的元素
        while (!q.empty() && q.front() < x - k[i]) q.pop_front();
        // 保证队列单调
        while (!q.empty() && f[last][q.back() * w[i] + y] - q.back() * v[i] < f[last][x * w[i] + y] - x * v[i]) q.pop_back();
        q.push_back(x);
        f[now][x * w[i] + y] = f[last][q.front() * w[i] + y] - q.front() * v[i] + x * v[i];
      }
    }
    swap(last, now);
  }
posted @ 2026-10-30 14:50  Rainesxiao  阅读(1)  评论(0)    收藏  举报