@RabbitHu
2021-12-11T12:43:31.000000Z
字数 1763
阅读 3707
题解
有种物品,每种物品的数量为。从中任选若干件放在容量为的背包里,每种物品的体积为(为整数),与之相对应的价值为(为整数)。求背包能够容纳的最大价值。
第1行,2个整数,和中间用空格隔开。为物品的种类,为背包的容量。(1 <= <= 100,1 <= <= 50000)
第2 - + 1行,每行3个整数,, 和 分别是物品体积、价值和数量。(1 <= <= 10000, 1 <= <= 200)
输出可以容纳的最大价值。
多重背包。
首先写出朴素的状态转移方程(表示前个物品、总体积为):
直接这样算的复杂度最差可达(极大、极小时)。
那么我们如何优化呢?考虑为、为、为时的计算过程:
提出一些,将以上三式整理一下:
可以发现,以上三式的计算中有许多重复的地方。
那么如何消除重复,达到优化呢?观察原始方程:
设,则上式等价于:
那么在每个i循环中,可以把所有的j按a分成w[i]组,每组新建一个队列,枚举b,每次把放进队列,并保持队列长度不超过c[i],用O(1)求出队列中最大值、从最大值转移即可。
#include <cstdio>#include <cmath>#include <cstring>#include <algorithm>#include <iostream>using namespace std;const int N = 103, M = 50003;int n, m, w[N], p[N], c[N], ans;//w[i]: 体积; p[i]: 价值; c[i]: 数量int dp[M], qa[M], qb[M], la, lb, ra, rb;//qa: 正常队列; qb: 用来求最大值的辅助队列int main(){scanf("%d%d", &n, &m);for(int i = 1; i <= n; i++)scanf("%d%d%d", &w[i], &p[i], &c[i]);for(int i = 1; i <= n; i++){for(int j = 0; j < w[i]; j++){la = lb = 0, ra = rb = -1;//清空队列for(int k = 0; j + k * w[i] <= m; k++){if(ra - la + 1 > c[i])//如果队列中元素个数超过限制if(qb[lb] == qa[la++]) lb++; //从队首弹出int x = dp[j + k * w[i]] - k * p[i];qa[++ra] = x;while(rb >= lb && qb[rb] < x) rb--;qb[++rb] = x;dp[j + k * w[i]] = qb[lb] + k * p[i];ans = max(ans, dp[j + k * w[i]]);}}}printf("%d\n", ans);return 0;}