zoukankan      html  css  js  c++  java
  • 多重背包问题 III

    题目来源:acwing6

    算法1
    (单调队列优化) O(NV)
    一共是n类物品,背包的容量是m
    每类物品的体积为v, 价值为w,个数为s

    我们先来回顾一下传统的dp方程

    dp[i][j] 表示将前 i 种物品放入容量为 j 的背包中所得到的最大价值
    dp[i][j] = max(不放入物品 i,放入1个物品 i,放入2个物品 i, ... , 放入k个物品 i)
    这里 k 要满足:k <= s, j - k*v >= 0
    
    不放物品  i = dp[i-1][j]
    放k个物品 i = dp[i-1][j - k*v] + k*w
    
    dp[i][j] = max(dp[i-1][j], dp[i-1][j-v] + w, dp[i-1][j-2*v] + 2*w,..., dp[i-1][j-k*v] + k*w)
    

    实际上我们并不需要二维的dp数组,适当的调整循环条件,我们可以重复利用dp数组来保存上一轮的信息

    我们令 dp[j] 表示容量为j的情况下,获得的最大价值
    那么,针对每一类物品 i ,我们都更新一下 dp[m] --> dp[0] 的值,最后 dp[m] 就是一个全局最优值

    dp[m] = max(dp[m], dp[m-v] + w, dp[m-2*v] + 2*w, dp[m-3*v] + 3*w, ...)

    接下来,我们把 dp[0] --> dp[m] 写成下面这种形式

    dp[0], dp[v],   dp[2*v],   dp[3*v],   ... , dp[k*v]
    dp[1], dp[v+1], dp[2*v+1], dp[3*v+1], ... , dp[k*v+1]
    dp[2], dp[v+2], dp[2*v+2], dp[3*v+2], ... , dp[k*v+2]
    ...
    dp[j], dp[v+j], dp[2*v+j], dp[3*v+j], ... , dp[k*v+j]
    

    显而易见,m 一定等于 kv + j,其中 0 <= j < v
    所以,我们可以把 dp 数组分成 j 个类,每一类中的值,都是在同类之间转换得到的
    也就是说,dp[k
    v+j] 只依赖于
    { dp[j], dp[v+j], dp[2*v+j], dp[3*v+j], ... , dp[k*v+j] }

    因为我们需要的是 { dp[j], dp[v+j], dp[2*v+j], dp[3*v+j], ... , dp[k*v+j] } 中的最大值,
    可以通过维护一个单调队列来得到结果。这样的话,问题就变成了 j 个单调队列的问题
    所以,我们可以得到

    dp[j]    =     dp[j]
    dp[j+v]  = max(dp[j] +  w,  dp[j+v])
    dp[j+2v] = max(dp[j] + 2w,  dp[j+v] +  w, dp[j+2v])
    dp[j+3v] = max(dp[j] + 3w,  dp[j+v] + 2w, dp[j+2v] + w, dp[j+3v])
    ...
    

    但是,这个队列中前面的数,每次都会增加一个 w ,所以我们需要做一些转换

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

    这样,每次入队的值是 dp[j+k*v] - k*w

    单调队列问题,最重要的两点
    1)维护队列元素的个数,如果不能继续入队,弹出队头元素
    2)维护队列的单调性,即:尾值 >= dp[j + kv] - kw

    本题中,队列中元素的个数应该为 s+1 个,即 0 -- s 个物品 i

    C++ 代码

    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    const int N = 20010;
    
    int dp[N], pre[N], q[N];
    int n, m;
    
    int main() {
        cin >> n >> m;
        for (int i = 0; i < n; ++i) {
            memcpy(pre, dp, sizeof(dp));
            int v, w, s;
            cin >> v >> w >> s;
            for (int j = 0; j < v; ++j) {
                int head = 0, tail = -1;
                for (int k = j; k <= m; k += v) {
    
                    if (head <= tail && k - s*v > q[head])
                        ++head;
    
                    while (head <= tail && pre[q[tail]] - (q[tail] - j)/v * w <= pre[k] - (k - j)/v * w)
                        --tail;
    
                    if (head <= tail)
                        dp[k] = max(dp[k], pre[q[head]] + (k - q[head])/v * w);
    
                    q[++tail] = k;
                }
            }
        }
        cout << dp[m] << endl;
        return 0;
    }
    
    
  • 相关阅读:
    Vue常用特性-表单基本操作和表单修饰符
    Tab选项卡
    Vue模板语法
    Vue基本使用
    Vue概述
    Express框架
    模板引擎artTemplate
    MongoDB增删改查操作
    数据库概述及环境搭建
    请求响应原理及HTTP协议
  • 原文地址:https://www.cnblogs.com/hnkjdx-ssf/p/14002566.html
Copyright © 2011-2022 走看看