E13 背包DP 多重背包 单调队列优化

E13 背包DP 多重背包 单调队列优化——信息学奥赛算法_哔哩哔哩_bilibili

 

E11【模板】单调队列 滑动窗口最值 - 董晓 - 博客园

P1776 宝物筛选 - 洛谷

$f_{i,j}$ 表示前 $i$ 种物品,背包承重不超过 $j$ 所获得的最大价值
\begin{aligned}
f_{i,j} & =\max\limits_{k=0}^{s}(f_{i-1,j-k \times w}+k \times v), \; 0 \leq j \leq W \\
f_{i,x \cdot w+y} & =\max\limits_{k=0}^{s}(f_{i-1,(x-k) \cdot w+y}+k \cdot v), \; ^{0 \leq y \leq w}_{0 \leq x \cdot w+y \leq W} \\
& =\max\limits_{k=0}^{s}(f_{i-1,(x-k) \cdot w+y}-(x-k)\cdot v+x \cdot v) \\
& =\max\limits_{k=0}^{s}(f_{i-1,(x-k) \cdot w+y}-(x-k)\cdot v)+x \cdot v \\
& =\max\limits_{p=x-s}^{x}(f_{i-1,p\cdot w+y}-p\cdot v)+x \cdot v \\
\end{aligned}
拆分背包承重:$j=x\cdot w+y$
外层枚举 $y$,内层枚举 $x$
单调队列维护前状态的最大值
因为 $0\leq k \leq s$,所以 窗口下标 $p=x-k$ 的范围:$x-s\leq p \leq x$

// 单调队列 O(n*W)
#include<bits/stdc++.h>
using namespace std;

const int N=40005;
int n,W;
int f[2][N];

int main(){
  ios::sync_with_stdio(0); cin.tie(0);
  cin>>n>>W;

  for(int i=1,v,w,s; i<=n; i++){   //物品
    cin>>v>>w>>s;                  //价值 重量 个数
    for(int y=0; y<w; y++){        //背包承重拆成整加零,即x*w+y
      deque<int> q;
      for(int x=0; x*w+y<=W; x++){ //定零变整,队列维护
        while(!q.empty() && q.front()<x-s) q.pop_front(); //窗口下标[x-s,x]
        while(!q.empty() && f[i-1&1][q.back()*w+y]-q.back()*v<f[i-1&1][x*w+y]-x*v) q.pop_back();
        q.push_back(x);
        f[i&1][x*w+y]=(f[i-1&1][q.front()*w+y]-q.front()*v)+x*v;
      }
    }
  }
  cout<<f[n&1][W];
}

 

// 单调队列 O(n*W)
#include<bits/stdc++.h>
using namespace std;

const int N=40005;
int n,W;
int f[N],g[N];

int main(){
  ios::sync_with_stdio(0); cin.tie(0);
  cin>>n>>W;

  for(int i=1,v,w,s; i<=n; i++){   //物品
    cin>>v>>w>>s;                  //价值 重量 个数
    memcpy(g,f,sizeof(f));
    for(int y=0; y<w; y++){        //背包承重拆成整加零,即x*w+y
      deque<int> q;
      for(int x=0; x*w+y<=W; x++){ //定零变整,队列维护
        while(!q.empty() && q.front()<x-s) q.pop_front(); //窗口下标[x-s,x]
        while(!q.empty() && g[q.back()*w+y]-q.back()*v<g[x*w+y]-x*v) q.pop_back();
        q.push_back(x);
        f[x*w+y]=(g[q.front()*w+y]-q.front()*v)+x*v;
      }
    }
  }
  cout<<f[W];
}

 

// 单调队列 O(n*W)
#include<bits/stdc++.h>
using namespace std;

const int N=40005;
int n,W;
int f[2][N];

int main(){
  ios::sync_with_stdio(0); cin.tie(0);
  cin>>n>>W;
  
  for(int i=1,v,w,s; i<=n; ++i){
    cin>>v>>w>>s;
    for(int y=0; y<w; ++y){
      deque<int> q;
      for(int x=y; x<=W; x+=w){
        while(!q.empty() && q.front()<x-s*w) q.pop_front();
        while(!q.empty() && f[i-1&1][q.back()]+(x-q.back())/w*v<f[i-1&1][x]) q.pop_back();
        q.push_back(x);
        f[i&1][x]=f[i-1&1][q.front()]+(x-q.front())/w*v;
      }
    }
  }
  cout<<f[n&1][W];
}

 

posted @ 2023-04-10 09:57  董晓  阅读(1367)  评论(0)    收藏  举报