题目传送门

董晓老师讲解

一、题目分析

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

#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;
}

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

二、二维朴素版本

#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;
}

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-10-19
  • 2021-06-17
  • 2022-02-19
  • 2022-12-23
  • 2021-08-25
  • 2021-11-10
猜你喜欢
  • 2022-03-07
  • 2021-06-07
  • 2021-06-17
  • 2021-08-07
  • 2022-12-23
相关资源
相似解决方案