潜水员
[问题描述]:
潜水员为了潜水要使用特殊的装备。他有一个带2 种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种的数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少?
例如:潜水员有5 个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
如果潜水员需要5 升的氧和60 升的氮则总重最小为249(1,2 或者4,5 号气缸)。
你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。
[输入格式]:
从文本文件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 个气缸里的氧和氮的容量及汽缸重量。
[输出格式]:
仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。
[样例输入]:
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
[样例输出]:
249
这道题乍一看是一道二维费用背包。但特殊的是,选的物品的体积可以超过背包的容积,比如容积10,你可以选15的体积的物品,然后求出最少的价值(就是这道题的重量)。
先回顾一下二维费用背包的转移方程
dp[i][j][k] = min(dp[i][j][k], dp[i - 1][j - c1[i]][k - c2[i]] + v[i])
只不过这道题还要考虑所选的物品体积大于背包容积的情况,所以最后我们应在dp[n][j][k](j >= t, k >= a)的所有状态中取min。
1 for(int j = 0; j < max_size; ++j) 2 for(int k = 0; k < max_size; ++k) dp[j][k] = INF; 3 dp[0][0] = 0; 4 for(int i = 1; i <= n; ++i) 5 for(int j = t + a[i].o2; j >= a[i].o2; --j) //注意j应该从t + a[i].o2枚举,因为这是选了当前物品能得到的最大体积 6 for(int k = q + a[i].n2; k >= a[i].n2; --k)//k同理 7 { 8 dp[j][k] = min(dp[j][k], dp[j - a[i].o2][k - a[i].n2] + a[i].w); 9 } 10 for(int j = t; j < max_size; ++j) 11 for(int k = q; k < max_size; ++k) 12 ans = min(ans, dp[j][k]); 13 printf("%d\n", ans);
还有一个优化,定义dp[i][t][a](题中的t, a)为选i个物品不小于t和a个体积时的重量。那么转移方程就变成
dp[i][min(t, j)][min(a, k)] = min(dp[i][min(t, j)][min(a, k)], dp[i - 1][j - c1[i]][k - c2[i]] + v[i])
对于所有的j,k超过了t和a,都应转移到dp[i][t][a].
1 for(int i = 1; i <= n; ++i) 2 for(int j = t + a[i].o2; j >= a[i].o2; --j) 3 for(int k = q + a[i].n2; k >= a[i].n2; --k) 4 { 5 int to2 = min(t, j), tn2 = min(q, k); 6 dp[to2][tn2] = min(dp[to2][tn2], dp[j - a[i].o2][k - a[i].n2] + a[i].w); 7 } 8 printf("%d\n", dp[t][q]);