You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##[$AcWing$ $1020$. 潜水员](https://www.acwing.com/problem/content/description/1022/)
### 一、题目描述
潜水员为了潜水要使用特殊的装备。
他有一个带$2$种气体的气缸:一个为氧气,一个为氮气。
让潜水员下潜的深度需要各种数量的氧和氮。
潜水员有一定数量的气缸。
每个气缸都有重量和气体容量。
潜水员为了完成他的工作需要特定数量的氧和氮。
他完成工作所需气缸的总重的 **最低限度** 的是多少?
例如:潜水员有$5$个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:
```cpp {.line-numbers}
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
```
如果潜水员需要$5$升的氧和$60$升的氮则总重最小为$249$$12$或者$45$号气缸)。
你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的 **最低值**。
**输入格式**
第一行有$2$个整数 $mn$。它们表示氧,氮各自需要的量。
第二行为整数 $k$ 表示气缸的个数。
此后的 $k$ 行,每行包括$a_ib_ic_i$$3$个整数。这些各自是:第 $i$ 个气缸里的氧和氮的容量及气缸重量。
**输出格式**
仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。
**数据范围**
$1≤m≤21,1≤n≤79,1≤k≤1000,1≤a_i≤21,1≤b_i≤79,1≤c_i≤800$
**输入样例**
```cpp {.line-numbers}
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
```
**输出样例**
```cpp {.line-numbers}
249
```
### 二、与普通二维背包费用问题的区别
* 普通的二维背包费用问题
$f(i,j,k)$ 表示从前$i$个物品中选,且花费$1$**不超过**$j$,花费$2$**不超过**$k$的 **最大** 价值
* 潜水员
$f(i,j,k)$ 表示从前$i$个物品中选,且花费$1$**不少于**$j$,花费$2$**不少于**$k$的 **最小** 价值
本题是一个 **二维费用$01$背包问题** ,但和一般的 **二维费用$01$背包问题** 不同
这题要求的是 <font color='red' size=4><b>费用不少于</b></font> 规定条件,因此我们需要对于 **状态的定义** 进行改变
#### 闫氏$DP$分析法
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/%7Byear%7D/%7Bmonth%7D/%7Bmd5%7D.%7BextName%7D/20230629155418.png)
这样分析完,似乎与普通的二维费用背包没有区别!这肯定是不可能的,我们必然是遗漏了些什么!!
<font color='red' size=6><b>考虑$j-v_1<0$,$k-v_2<0$的情况!</b></font>
有普通的二维费用背包问题中,$j,k$是不能进行超载的,超过了背包就太重, 背包就 **漏** 了!
在本题中是 **可以超载** 的,理解一下超载是什么意思:
> - $j$:氧气还需要$j$升
> - $k$:氮气还需要$k$升
**举栗子**$j=2,k=5$,就是氧气还需要$2$升,氮气还需要$5$升,现在出现的某个气瓶,氧气$20$升,氮气$50$升,一个就可以把你的需求满足,那么请问:你还需要氧气多少升、氮气多少升呢?
**答**:不需要,都可以满足要求了,即$j=0,k=0$,也就是$f[i-1][0][0]+w$,而对于一个无欲无求的$f[i-1][0][0]$自然是等于$0$,也就是$f[i][j][k]=w$
### 三、三维解法
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
const int M = 110;
int n, m, m1, m2;
int f[N][M][M];
int main() {
cin >> m1 >> m2 >> n;
memset(f, 0x3f, sizeof f);
f[0][0][0] = 0;
for (int i = 1; i <= n; i++) {
int v1, v2, w;
cin >> v1 >> v2 >> w;
for (int j = 0; j <= m1; j++)
for (int k = 0; k <= m2; k++) {
f[i][j][k] = f[i - 1][j][k];
f[i][j][k] = min(f[i - 1][j][k], f[i - 1][max(0, j - v1)][max(0, k - v2)] + w);
}
}
cout << f[n][m1][m2] << endl;
return 0;
}
```
### 四、二维解法
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 22, M = 80;
int n, m, K;
int f[N][M];
int main() {
cin >> n >> m >> K;
memset(f, 0x3f, sizeof f);
f[0][0] = 0;
while (K--) {
int v1, v2, w;
cin >> v1 >> v2 >> w;
for (int i = n; i >= 0; i--)
for (int j = m; j >= 0; j--)
f[i][j] = min(f[i][j], f[max(0, i - v1)][max(0, j - v2)] + w);
}
cout << f[n][m] << endl;
return 0;
}
```