题解:潜水员问题(二维费用,二维约束,背包)

潜水员问题

Description

潜水员为了潜水要使用特殊的装备。他有一个带2种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种的数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少?

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

3 36 120

10 25 129

5 50 250

1 45 130

4 20 119

如果潜水员需要5升的氧和60升的氮则总重最小为249 (1,2或者4,5号气缸)。

你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。

Input

从文本文件gas.in中读入数据。

第一行有2整数t,a(1<=t<=21,1<=a<=79)。它们表示氧,氮各自需要的量。

第二行为整数n (1<=n<=1000)表示气缸的个数。

此后的n行,每行包括ti,ai,wi(1<=ti<=21,1<=ai<=79,1<=wi<=800)3整数。这些各自是:第i个气缸里的氧和氮的容量及汽缸重量。

Output

仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。

Sample Input

5 60

5

3 36 120

10 25 129

5 50 250

1 45 130

4 20 119

Sample Output

249

解题思路整理:

变量说明:

首先,针对题目中的概念,重新定义变量(便于与以前的问题对照)

V,U,n分别代表氧气、氮气需求量、气缸个数

v[i],u[i],w[i]分别为i号气缸的氧气量、氮气量、气缸重量

(后续分析过程和程序就不再用题中所给变量)

题目类型:

这是一个二维约束条件下的01背包问题,

约束条件:氧气需求量j,氮气需求量k

目标条件:满足约束条件的最小气缸重量

思路一(倒查):

定义f[i][j][k] 表示有前i个气缸可供选取,满足氧气需求量为j、氮气需求量为k条件下,气缸的最小重量。

对于i号气缸,可做2种决策,①不选取i号气缸,②选取i号气缸。两种决策并不是都做,我们只做最优的决策,即执行后对应气缸总重最小的一个决策。

据此,得到基本状态转移方程

f[i][j][k] = min{ f[i-1][j][k] , f[i-1][j-v[i]][k-u[i]] }

决策①很明显了,决策②倒需要好好分析。根据经验,我们会首先想到边界问题。按照常识,j-v[i] 小于0是会出错的,k-u[i]小于0也会出错(数组下标不能小于0),那么当 j-v[i]<0 或 k-u[i]<0 时,我们该怎样来处理下标?

a.首先分析氧气需求量,当j<v[i]时,说明仅i号气缸就包含了氧气需求量j,前i-1个气缸不需要装氧气。因此,当j<v[i],k>=u[i]时,对应气缸总重为f[i-1][0][k-u[i]] +w[i];

b.同理,当j>=v[i],k<u[i] 时,对应气缸总重为 f[i-1][j-v[i]][0] +w[i];

c.当j<v[i],k<u[i]时,表示不需要前i-1个气缸了(i号气缸已包含所需氧气和氮气),此时,气缸总重为w[i].

D.当j>=v[i],k>=u[i]时,气缸总重为f[i-1][j-v[i]][k-u[i]]

根据上述分析,汇总状态转移方程:

wps4872.tmp

编写程序时,我们可以用多个if,else来实现这个状态转移方程,如果觉得代码啰嗦,可以参考下面写法:

wps4873.tmp

还要注意一点,就是状态初始值设定:

wps4884.tmp

思路二(正推):

f[i][j][k](0<=j<=V,0<=k<=U)的每一组状态值构成了一个二维矩阵。其中第i组状态值产生方式有以下两种:

wps4885.tmp

现在理一下思路二。

f[i-1][j][k] 为已知状态值(已推导出),现在需要求的是f[i][j+v[i]][k+u[i]],对应的含义为:前i个气缸可供选择,满足氧气需求为j+v[i] ,氮气需求为k+u[i] 条件下,气缸的最小重量。基本状态转移方程如下(这里状态初始化与思路一一致):

f[i][j+v[i]][k+u[i]] = min { f[i-1][j+v[i]][k+u[i]] , f[i-1][j][k] +w[i]}

这个方程代表的思想是:依据第i-1组状态中每个状态值,推导出第i组状态的某个状态值,如果新值优于(小于)旧值,则新值替换旧值;第i-1组所有状态值枚举完后,第i组状态的每个值也产生了。

同样,在做决策②的时候,我们也需要考虑边界值问题,推导过程可能会出现j+v[i]>V 或者 k+u[i]>U情况,同样会产生下标越界错误,因此,下面需要对j+v[i] 和 k+u[i] 的结果做一些处理:

a.如果 j+v[i]>V,说明选取i号气缸后,得到氧气量超过了需求量(这个可以有),因为此种情况满足了氧气需求,所以待计算的第i组状态为f[i][V][k+u[i]];

b.同理,如果k+u[i]>U,待计算的第i组状态为f[i][V][k+u[i]];

这个思路的程序写法参考如下:

wps4886.tmp

补充(状态压缩):

三维数组状态毕竟会消耗较大内存,如果可以压缩为二维就好了。

按照三维数组结构,可以存储共n+1组(包括第0组)状态,第i组状态由i-1组状态推出。然而,推出第i组状态后,第i-1组状态就再没有被用到了。由此,我们想到用第i组状态覆盖第i-1组状态,这样,数组第一维就被压缩了。但是要注意推导顺序,循环过程j,k须由大到小,这样才能确保第i组状态值是由第i-1组状态值推导出来的。

如果循环过程j,k由小到大会怎样呢?如果这样,第i组靠前的部分状态会先被计算出来(覆盖掉第i-1组相应状态),在推导第i组靠后的部分状态时,依赖的数值已经被更改,结果就会出错;而循环过程j,k由大到小,则不存在这个问题。

代码参考如下:

image

image

posted @ 2017-09-19 09:09  akangmaker  阅读(1186)  评论(0编辑  收藏  举报