动态规划之单调队列优化(多重背包)

对于多重背包一题
题目链接

题目大意:
这道题目是一道多重背包的模板题。
首先告诉你 n 件物品和背包的容量 V ,然后分别告诉你 n 件物品的价值 w 、体积 v 以及数量 m ,求解这个背包能够装载的最大价值是多少

解析思路:
定义 dpj 为前 i 个物品,在背包储存容量为 j 的最大价值, q 为选择当前物品个数

显然 dp[j] = max(dp[j], dp[j - w] + v, dp[j - 2w] +2v, ... dp[j - qw] + qv (j >= qw && q <= m)

换一种写法 dpj 可向 dpj+qw 转移, 即满足 dpidpj 转移的条件是 \(i \equiv j \,(mod\,w)\)

如果只是这样的话, 那么显然 \(dp_j\) 只需要在任意 \(mod/,w\) 同余的位置转移过来即可

上面的写法适用于完全背包,但多重背包是有物品数量限制的,\(dp_j\) 最小从 \(dp_{j - qw}\) 转移过来 \((q <= m)\) 也就是最多前 m 个位置

在这里我们相求前 m 个数的最大值就考虑使用单调队列来优化了

但是还有新的问题

dp[j] = max(dp[j]);
dp[j + w] = max(dp[j + w], dp[j] + v);
dp[j +2w] = max(dp[j +2w], dp[j + w] + v,dp[j] + 2v);
dp[j +3w] = max(dp[j +3w], dp[j +2w] + v, dp[j + w] + 2v, dp[j] + 3v);
...
dp[j +qw] = max(dp[j +qw],..., dp[j +3w] +(q-3)v, dp[j +2w] +(q-2)v, dp[j + w] +(q-1)v, dp[j] + qv);

注意到上面式子 每个位置所求前面的值都不同,显然无法进行单调队列优化,我们考虑将所有共同部分提出来

dp[j] = max(dp[j]);
dp[j + w] = max(dp[j + w] - v, dp[j]) + v;
dp[j +2w] = max(dp[j +2w] -2v, dp[j + w] - v,dp[j]) +2v;
dp[j +3w] = max(dp[j +3w] -3v, dp[j +2w] -2v, dp[j + v] - v, dp[j]) +3v;
...
dp[j +qw] = max(dp[j +qw] -qv,..., dp[j +3w] -3v,dp[j +2w] -2v,dp[j + w] -v, dp[j]) +qv;

注意到只要转移来的位置是一定的,那么求最值( max 括号内)的内容是一样的(和转移到的位置没有关系)

最后别忘记单调队列中最大窗口是m, 下面附上完整代码

#include<bits/stdc++.h>
using namespace std;

using ll = long long;

void solve(){
    ll n, W;
    cin >> n >> W;
    vector<ll> v(n + 1);   
    vector<ll> w(n + 1);   
    vector<ll> m(n + 1);
    vector<ll> dp(W + 1);   
    for(ll i = 1; i <= n; i++){
        cin >> v[i] >> w[i] >> m[i];
    }
    for(ll i = 1; i <= n; i++){
        vector<ll> dp1 = dp;
        for(ll j = 0; j < w[i]; j++){
            deque<ll> q;//存储索引

            for(ll k = j; k <= W; k += w[i]){
                while(q.size() && ((k - q.front())/w[i] > m[i]))q.pop_front();
                while(q.size() && (dp1[k] >= dp1[q.back()] + (k - q.back())/w[i]*v[i]))q.pop_back();
                q.push_back(k);
                dp[k] = max(dp1[k], dp1[q.front()] + ((k - q.front()) / w[i]) * v[i]);
            }
        }
    }   
    ll ans = 0;
    for(ll i = 0; i <= W; i++){
        ans = max(ans, dp[i]);
    }
    cout << ans << "\n";
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    solve();
}
posted @ 2025-08-28 01:52  DUC27  阅读(45)  评论(0)    收藏  举报