• AcWing 6 多重背包问题III【单调对列优化】


    题目传送门

    董晓老师讲解

    一、题目分析

    使用朴素版本利用数据进行调试,找一下规律,看看哪个状态间存在转移关系:

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 1e5 + 10;
    int n, m;
    int v, w, s;
    int f[N];
    
    /**
     * 测试用例:
     2 9
     3 5 2
     2 4 3
     */
    int main() {
        cin >> n >> m;
        //多重背包朴素算法[一维空间]
        for (int i = 1; i <= n; i++) {
            cin >> v >> w >> s; //体积、价值、数量
            //一维是倒序,而且最小值可以到达v
            for (int j = m; j >= v; j--)
                for (int k = 0; k <= s && j >= k * v; k++) {
                    f[j] = max(f[j], f[j - k * v] + k * w);
                    //输出中间过程,用于调试,找规律
                    printf("f[%d]=%2d f[%d]+%d=%d\n", j, f[j], j - k * v, k * w, f[j - k * v] + k * w);
                }
        }
        return 0;
    }
    

    二、二维朴素版本

    #include <bits/stdc++.h>
    
    using namespace std;
    //题解
    //https://www.acwing.com/solution/content/53507/
    
    const int N = 1010; //物品种类上限
    const int M = 20010;//背包容量上限
    int n, m;
    int v[N], w[N], s[N];
    int f[N][M];        //前i个物品,在容量为j的限定下,最大的价值总和
    int q[M];           //单调优化的队列
    
    //多重背包1 —朴素版本多重背包
    //多重背包2 —二进制优化版本多重背包
    //多重背包3 —单调队列优化
    
    //二维朴素版
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; i++) cin >> v[i] >> w[i] >> s[i];
    
        for (int i = 1; i <= n; i++)                //枚举i个物品
            for (int r = 0; r < v[i]; r++) {        //枚举余数,分组
                int hh = 0, tt = -1;                //初始化单调队列,队列中装的是前序可以转化过来的体积
                for (int j = r; j <= m; j += v[i]) {
                    //j和q[hh]之间可以装的下的物品个数,一旦大于固定数量,队首出队
                    while (hh <= tt && j - q[hh] > s[i] * v[i]) hh++;
                    while (hh <= tt && f[i - 1][q[tt]] + (j - q[tt]) / v[i] * w[i] <= f[i - 1][j]) tt--;
                    q[++tt] = j;                    //入队列
                    //更新
                    f[i][j] = f[i - 1][q[hh]] + (j - q[hh]) / v[i] * w[i];
                }
            }
        printf("%d", f[n][m]);
        return 0;
    }
    
    

    三、滚动数组优化空间

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1010, M = 20010;
    //滚动数组写法
    int n, m;
    int v[N], w[N], s[N];
    int f[2][M];
    int q[M];
    
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; i++) cin >> v[i] >> w[i] >> s[i];
        for (int i = 1; i <= n; i++)
            for (int r = 0; r < v[i]; r++) {
                int hh = 0, tt = -1;//队列中放的是前面s个可能转移过来的剩余空间
                for (int j = r; j <= m; j += v[i]) {
                    //j和q[hh]之间可以装的下的物品个数,一旦大于固定数量,队首出队
                    while (hh <= tt && j - q[hh] > s[i] * v[i]) hh++;
                    while (hh <= tt && f[(i - 1) & 1][q[tt]] + (j - q[tt]) / v[i] * w[i] <= f[(i - 1) & 1][j]) tt--;
                    q[++tt] = j;
                    f[i & 1][j] = f[(i - 1) & 1][q[hh]] + (j - q[hh]) / v[i] * w[i];
                }
            }
        cout << f[n & 1][m] << endl;
        return 0;
    }
    

    四、数组拷贝版本

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1010, M = 20010;
    
    int n, m;
    int v[N], w[N], s[N];
    int f[M], g[M];
    int q[M];
    //拷贝数组写法
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; i++) cin >> v[i] >> w[i] >> s[i];
        for (int i = 1; i <= n; i++) {
            memcpy(g, f, sizeof g);
            for (int r = 0; r < v[i]; ++r) {
                int hh = 0, tt = -1;
                for (int j = r; j <= m; j += v[i]) {
                    while (hh <= tt && j - q[hh] > s[i] * v[i]) hh++;
                    while (hh <= tt && g[q[tt]] + (j - q[tt]) / v[i] * w[i] <= g[j]) tt--;
                    q[++tt] = j;
                    f[j] = g[q[hh]] + (j - q[hh]) / v[i] * w[i];
                }
            }
        }
        cout << f[m] << endl;
        return 0;
    }
    
    
  • 相关阅读:
    cocos2dx环境搭建(android平台)
    转:Android开发实践:Java层与Jni层的数组传递
    转:Linux串口编程入门详解
    Linux串口通信之termios结构体说明
    SQLite3 C语言API入门
    android常用工具记录
    Java中byte与16进制字符串的互相转换
    JNI原理及实现
    Java调用动态库方法说明
    JAVA中使用动态链接库须知
  • 原文地址:https://www.cnblogs.com/littlehb/p/15670962.html
Copyright © 2020-2023  润新知