|
|
|
|
##[$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$($1,2$或者$4,5$号气缸)。
|
|
|
|
|
|
|
|
|
|
你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的 **最低值**。
|
|
|
|
|
|
|
|
|
|
**输入格式**
|
|
|
|
|
第一行有$2$个整数 $m,n$。它们表示氧,氮各自需要的量。
|
|
|
|
|
|
|
|
|
|
第二行为整数 $k$ 表示气缸的个数。
|
|
|
|
|
|
|
|
|
|
此后的 $k$ 行,每行包括$a_i,b_i,c_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$分析法
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这样分析完,似乎与普通的二维费用背包没有区别!这肯定是不可能的,我们必然是遗漏了些什么!!
|
|
|
|
|
|
|
|
|
|
<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 f[N][M][M];
|
|
|
|
|
int n, m1, m2;
|
|
|
|
|
|
|
|
|
|
//二维费用01背包-不少于维度费用,求最小代价
|
|
|
|
|
int main() {
|
|
|
|
|
//注意次序
|
|
|
|
|
scanf("%d %d %d", &m1, &m2, &n);
|
|
|
|
|
//求最小值
|
|
|
|
|
memset(f, 0x3f, sizeof f);
|
|
|
|
|
f[0][0][0] = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
int v1, v2, w;
|
|
|
|
|
scanf("%d %d %d", &v1, &v2, &w);
|
|
|
|
|
for (int j = 0; j <= m1; j++)
|
|
|
|
|
for (int k = 0; k <= m2; k++) {
|
|
|
|
|
//不选择i号物品
|
|
|
|
|
f[i][j][k] = f[i - 1][j][k];
|
|
|
|
|
//分情况讨论
|
|
|
|
|
|
|
|
|
|
//物品i加上就够一维使用,此时,只关心二维情况即可
|
|
|
|
|
if (j - v1 < 0 && k - v2 >= 0)
|
|
|
|
|
f[i][j][k] = min(f[i][j][k], f[i - 1][0][k - v2] + w);
|
|
|
|
|
//物品i加上就够二维使用,此时,只关心一维情况即可
|
|
|
|
|
else if (j - v1 >= 0 && k - v2 < 0)
|
|
|
|
|
f[i][j][k] = min(f[i][j][k], f[i - 1][j - v1][0] + w);
|
|
|
|
|
//如果选择了i号物品,两个维度直接拉满,那么只选择一个i就足够用,它参选的价值是w
|
|
|
|
|
else if (j - v1 < 0 && k - v2 < 0)
|
|
|
|
|
f[i][j][k] = min(f[i][j][k], w);
|
|
|
|
|
else
|
|
|
|
|
//正常递推
|
|
|
|
|
f[i][j][k] = min(f[i][j][k], f[i - 1][j - v1][k - v2] + w);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
printf("%d\n", f[n][m1][m2]);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
```
|