|
|
|
@ -372,7 +372,6 @@ int main() {
|
|
|
|
|
$0<N,V≤100$
|
|
|
|
|
$0<v_i,w_i,s_i≤100$
|
|
|
|
|
|
|
|
|
|
**二维数组**
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
using namespace std;
|
|
|
|
@ -395,33 +394,14 @@ int main() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**一维数组**
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
const int N = 110;
|
|
|
|
|
|
|
|
|
|
int n, m;
|
|
|
|
|
int f[N];
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
cin >> n >> m;
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
int v, w, s;
|
|
|
|
|
cin >> v >> w >> s;
|
|
|
|
|
for (int j = m; j >= v; j--)
|
|
|
|
|
for (int k = 0; k <= s && k * v <= j; k++)
|
|
|
|
|
f[j] = max(f[j], f[j - v * k] + w * k);
|
|
|
|
|
}
|
|
|
|
|
printf("%d\n", f[m]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $5$. 多重背包问题 II](https://www.acwing.com/problem/content/description/5/)**
|
|
|
|
|
|
|
|
|
|
**数据范围**
|
|
|
|
|
$0<N≤1000$
|
|
|
|
|
$0<V≤2000$
|
|
|
|
|
$0<v_i,w_i,s_i≤2000$
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
using namespace std;
|
|
|
|
@ -429,24 +409,17 @@ using namespace std;
|
|
|
|
|
const int N = 1010; // 个数上限
|
|
|
|
|
const int M = 2010; // 体积上限
|
|
|
|
|
int n, m, idx;
|
|
|
|
|
int f[M];
|
|
|
|
|
// 无法使用二维数组,原因是因为分拆后N*31*M=31*1010*2010太大了,MLE了
|
|
|
|
|
// 所以,需要使用滚动数组进行优化一下,思想还是二维的
|
|
|
|
|
int f[2][M];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Q:为什么是N*12?
|
|
|
|
|
A:本题中v_i<=2000,因为c数组是装的打包后的物品集合,每类物品按二进制思想,2000最多可以打log2(2000)+1个包,即 10.96578+1=12个足够,
|
|
|
|
|
同时,共N类物品,所以最大值是N*12。
|
|
|
|
|
|
|
|
|
|
如果题目要求v_i<=INT_MAX,那么就是log2(INT_MAX)=31,开31个足够,因为31是准确的数字,不需要再上取整。
|
|
|
|
|
为保险起见,可以不用计算数组上限,直接N*32搞定!
|
|
|
|
|
*/
|
|
|
|
|
struct Node {
|
|
|
|
|
int w, v;
|
|
|
|
|
} c[N * 12];
|
|
|
|
|
} c[N * 31];
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
cin >> n >> m;
|
|
|
|
|
|
|
|
|
|
// 多重背包的经典二进制打包办法
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
int v, w, s;
|
|
|
|
|
cin >> v >> w >> s;
|
|
|
|
@ -457,12 +430,69 @@ int main() {
|
|
|
|
|
// 不够下一个2^n时,独立成包
|
|
|
|
|
if (s) c[++idx] = {s * w, s * v};
|
|
|
|
|
}
|
|
|
|
|
// 按01背包跑
|
|
|
|
|
|
|
|
|
|
// 按01背包跑就可以啦
|
|
|
|
|
for (int i = 1; i <= idx; i++)
|
|
|
|
|
for (int j = m; j >= c[i].v; j--) // 倒序
|
|
|
|
|
f[j] = max(f[j], f[j - c[i].v] + c[i].w);
|
|
|
|
|
for (int j = 1; j <= m; j++) {
|
|
|
|
|
f[i & 1][j] = f[i - 1 & 1][j];
|
|
|
|
|
if (j >= c[i].v)
|
|
|
|
|
f[i & 1][j] = max(f[i & 1][j], f[i - 1 & 1][j - c[i].v] + c[i].w);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 输出
|
|
|
|
|
printf("%d\n", f[m]);
|
|
|
|
|
printf("%d\n", f[idx & 1][m]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $6$. 多重背包问题 $III$](https://www.acwing.com/problem/content/6/)**
|
|
|
|
|
|
|
|
|
|
**数据范围**
|
|
|
|
|
$0<N≤1000$
|
|
|
|
|
|
|
|
|
|
$0<V≤20000$
|
|
|
|
|
|
|
|
|
|
$0<v_i,w_i,s_i≤20000$
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
const int N = 1010; // 物品种类上限
|
|
|
|
|
const int M = 20010; // 背包容量上限
|
|
|
|
|
int n, m;
|
|
|
|
|
|
|
|
|
|
int f[N][M]; // 前i个物品,在容量为j的限定下,最大的价值总和
|
|
|
|
|
int q[M]; // 单调优化的队列,M是背包容量上限,说明q[]里面保存的是体积
|
|
|
|
|
|
|
|
|
|
// 二维+队列[k-s*v,k],队列长s+1
|
|
|
|
|
int main() {
|
|
|
|
|
cin >> n >> m;
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= n; i++) { // 考虑前i种物品
|
|
|
|
|
int v, w, s; // 体积、价值、个数
|
|
|
|
|
cin >> v >> w >> s;
|
|
|
|
|
// 下面的j,k是一起用来描述剩余体积的,之所以划分成两层循环,是因为依赖的前序是按v为间隔的依赖,并且,是有个数限制的依赖
|
|
|
|
|
// j:按对体积取模分组:0表示剩余空间除以当前物品的体积余数是0
|
|
|
|
|
// k:分组内的每一个体积,注意:这里的体积不一定都是合法的,因为数量是有限制的
|
|
|
|
|
// 单调队列的意义:查找前面k-s*v范围内的价值的最大值,是一个单调递减的队列,队头保存的是获取到最大值的最近体积
|
|
|
|
|
for (int j = 0; j < v; j++) { // 按余数分组讨论
|
|
|
|
|
int hh = 0, tt = -1; // 全新的单调下降队列
|
|
|
|
|
for (int k = j; k <= m; k += v) { // 与j一起构成了有效体积
|
|
|
|
|
// 1、讨论到第i个物品时,由于它最多只有s个,所以有效的转移体积最小是k-s*v,更小的体积将被去除
|
|
|
|
|
if (hh <= tt && q[hh] < k - s * v) hh++;
|
|
|
|
|
// 2、处理队尾,下一个需要进入队列的是f[i-1][k],它是后来的,生命周期长,可以干死前面能力不如它的所有老头子,以保证一个单调递减的队列
|
|
|
|
|
while (hh <= tt && f[i - 1][q[tt]] + (k - q[tt]) / v * w <= f[i - 1][k]) tt--;
|
|
|
|
|
// 3、k入队列
|
|
|
|
|
q[++tt] = k;
|
|
|
|
|
// 4、队列维护完毕,f[i-1][k]已经进入队列,f[i][k]可以直接从队头取出区间最大值更新自己
|
|
|
|
|
f[i][k] = f[i - 1][q[hh]] + (k - q[hh]) / v * w;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
printf("%d\n", f[n][m]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|