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;
    }
    
    
  • 相关阅读:
    SharePoint 2010 User Profile Sync Service自动停止
    如何区别多个svchost.exe?
    Log Parser分析IIS log的一个简单例子
    Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
    Windows中右键点击文件夹, 结果找不到共享选项卡, 怎么办?
    介绍SOS中的SaveModule命令
    SharePoint中Draft版本的文档不会收到document added的Alert Email
    和我一起学Windows Workflow Foundation(1)创建和调试一个WF实例
    门户网站
    C#基础—— check、lock、using语句归纳
  • 原文地址:https://www.cnblogs.com/hnkjdx-ssf/p/14002566.html
Copyright © 2011-2022 走看看