main
黄海 1 year ago
parent 832086d29d
commit f190562b4d

@ -372,7 +372,6 @@ int main() {
$0<N,V100$
$0<v_i,w_i,s_i100$
**二维数组**
```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<N1000$
$0<V2000$
$0<v_i,w_i,s_i2000$
```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<N1000$
$0<V20000$
$0<v_i,w_i,s_i20000$
```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;
}
```
Loading…
Cancel
Save