题目地址:
https://www.acwing.com/problem/content/1022/
潜水员为了潜水要使用特殊的装备。他有一个带 2 2 2种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少?例如:潜水员有 5 5 5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
如果潜水员需要 5 5 5升的氧和 60 60 60升的氮则总重最小为 249 249 249( 1 1 1, 2 2 2或者 4 4 4, 5 5 5号气缸)。你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。
输入格式:
第一行有
2
2
2个整数
m
m
m,
n
n
n。它们表示氧,氮各自需要的量。第二行为整数
k
k
k表示气缸的个数。此后的
k
k
k行,每行包括
a
i
a_i
ai,
b
i
b_i
bi,
c
i
c_i
ci,
3
3
3个整数。这些各自是:第
i
i
i个气缸里的氧和氮的容量及气缸重量。
输出格式:
仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。
数据范围:
1
≤
m
≤
21
1≤m≤21
1≤m≤21
1
≤
n
≤
79
1≤n≤79
1≤n≤79
1
≤
k
≤
1000
1≤k≤1000
1≤k≤1000
1
≤
a
i
≤
21
1≤ai≤21
1≤ai≤21
1
≤
b
i
≤
79
1≤bi≤79
1≤bi≤79
1
≤
c
i
≤
800
1≤ci≤800
1≤ci≤800
思路是动态规划。设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]是只取前 i i i个气缸的情况下,要含氧量大于等于 j j j并且含氮量大于等于 k k k,所有方案里的气缸最小重量。那么可以按照第 i i i个气缸取不取来分类,如果不取,那么最小重量就是 f [ i − 1 ] [ j ] [ k ] f[i-1][j][k] f[i−1][j][k];如果取,那么除了这个气缸以外,剩余气缸的含氧量只需要达到 max { j − a i , 0 } \max\{j-a_i,0\} max{j−ai,0},并且含氮量只需要达到 max { k − b i , 0 } \max\{k-b_i, 0\} max{k−bi,0}即可(也就是说 f [ i − 1 ] [ max { j − a i , 0 } ] [ max { k − b i , 0 } ] + c i f[i-1][\max\{j-a_i,0\}][\max\{k-b_i,0\}]+c_i f[i−1][max{j−ai,0}][max{k−bi,0}]+ci一定已经考虑了所有含第 i i i个气缸的方案了)。所以有: f [ i ] [ j ] [ k ] = min { f [ i − 1 ] [ j ] [ k ] , f [ i − 1 ] [ max { j − a i , 0 } ] [ max { k − b i , 0 } ] + c i } f[i][j][k]=\min\{f[i-1][j][k],f[i-1][\max\{j-a_i,0\}][\max\{k-b_i,0\}]+c_i\} f[i][j][k]=min{f[i−1][j][k],f[i−1][max{j−ai,0}][max{k−bi,0}]+ci}可以用类似 0 − 1 0-1 0−1背包的方式优化,只存两维,然后每个内层循环从大到小遍历。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 25, M = 80;
// n是氧需要的量,m是氮需要的量,K是气缸个数
int n, m, K;
int f[N][M];
int main() {
cin >> n >> m >> K;
// 初始化为正无穷
memset(f, 0x3f, sizeof f);
// 没有气缸可供选择,并且要求含氧量和含氮量都大于等于0,什么都不选是个合法方案,其总重量是0
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(i - v1, 0)][max(j - v2, 0)] + w);
}
cout << f[n][m] << endl;
return 0;
}
时间复杂度 O ( m n k ) O(mnk) O(mnk),空间 O ( m n ) O(mn) O(mn)。