其他分享
首页 > 其他分享> > 潜水员

潜水员

作者:互联网

潜水员

潜水员为了潜水要使用特殊的装备。

他有一个带 $2$ 种气体的气缸:一个为氧气,一个为氮气。

让潜水员下潜的深度需要各种数量的氧和氮。

潜水员有一定数量的气缸。

每个气缸都有重量和气体容量。

潜水员为了完成他的工作需要特定数量的氧和氮。

他完成工作所需气缸的总重的最低限度的是多少?

例如:潜水员有 $5$ 个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:

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 \leq m \leq 21$,
$1 \leq n \leq 79$,
$1 \leq k \leq 1000$,
$1 \leq a_i \leq 21$,
$1 \leq b_i \leq 79$,
$1 \leq c_i \leq800$

输入样例:

5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119

输出样例:

249

 

解题思路

  这是一个二维费用的背包问题。与之前的背包问题不同的是,这里要求费用$1$至少是多少,费用$2$至少是多少。

  下面先给出一种状态表示,但根据这种状态表示来进行计算会超时。

  $f \left( {i, j, k} \right)$表示从前$i$个物品中选,氧气的容量恰好为$j$,氮气的容量恰好为$k$的所有方案的集合,属性是重量的最小值。状态转移方程为$$f \left( {i, j, k} \right) = min \{ f \left( {i - 1, j, k} \right),~ f \left( {i - 1, j - v_{1i}, k - v_{2i}} \right) + w_i \}$$

  其中$j$和$k$的最大上限均为$21 \times 79$,这是因为可能会有极端的数据,比如输入的氧气和氮气的容量全是“$1~79$”或者“$21~1$”。因此计算量就是$1000 \times \left( {21 \times 79} \right)^{2}$,必然会超时,所以需要换一种状态表示。

  状态表示还是$f \left( {i, j, k} \right)$,不过集合的含义变了,表示从前$i$个物品中选,氧气的容量至少为$j$,氮气的容量至少为$k$的所有方案的集合,属性是重量的最小值。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 30, M = 90;
 5 
 6 int f[N][M];
 7 
 8 int main() {
 9     int n, m1, m2;
10     scanf("%d %d %d", &m1, &m2, &n);
11     
12     memset(f, 0x3f, sizeof(f));
13     f[0][0] = 0;    // 一开始从前0个物品中选,也就是没有,氧气氮气的容量均为0,其他的状态俊不合法
14     for (int i = 1; i <= n; i++) {
15         int v1, v2, w;
16         scanf("%d %d %d", &v1, &v2, &w);
17         for (int j = m1; j >= 0; j--) {
18             for (int k = m2; k >= 0; k--) { // j和k均循环到0,即使k-v1<0或k-v2<0也是合法的
19                 // 得到负数时说明只选择第i个物品就可以达到最小量j或k,意味着之前没有选含这个气体的物品,因此可以取0
20                 f[j][k] = min(f[j][k], f[max(0, j - v1)][max(0, k - v2)] + w);
21             }
22         }
23     }
24     
25     printf("%d", f[m1][m2]);
26     
27     return 0;
28 }

   代码中取$max$的原因是,每个物品装有的气体的容量都大于等于$1$,因此当某个气体减去当前瓶子的气体容量后小于$0$,是等价于$0$的,因为当容量小于$0$意味着一个瓶子都没有选,而每个瓶子的装有气体的容量大于等于$1$。

 

参考资料 

  AcWing 1020. 潜水员(算法提高课):https://www.acwing.com/video/379/

标签:right,气缸,容量,int,leq,潜水员
来源: https://www.cnblogs.com/onlyblues/p/16513532.html