1 题面
1.1 描述
潜水员为了潜水要使用特殊装备。他有一个带两种气体的气缸:一个为氮气,一个为氧气。让潜水员下潜的深度需要各种数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定的氧和氮。他完成工作所需气缸的总重最低限度是多少?
例如:潜水员有5个气缸。每行有三个数字为:氧、氮的量(升)和气缸的重量:
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
如果潜水员需要5升的氧和60升的氮则总重的最小值为249(1,2或4,5号气缸)。
你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。
1.2 输入
第一行有2个整数m,n(1<=m<=21,1<=n<=79)。它们表示氧、氮各自需要的量。
第二行为整数k(1<=k<=1000)表示气缸的个数。
此后的k行,每行包括ai,bi,ci(1<=ai<=21,1<=bi<=79,1<=ci<=800)3整数
这些各自是:第i个气缸里的氧和氮的容量及气缸重量。
1.3 输出
仅一行,包含一个整数,为潜水员完成工作所需气缸的重量总和的最低值。
1.4 样例
输入
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
输出
249
1.5 范围
1<=m<=21,1<=n<=79 1<=k<=1000 1<=ai<=21,1<=bi<=79,1<=ci<=800
1.6 来源
石室联中WOJ
2 分析
2.1 第一次分析
求最低值,有下限?
反过来不就好了,转换成扔罐子,而上限便是总量减去需要的量,便是一个裸的二维背包
然而…
21
⋅
1000
⋅
79
⋅
1000
21\cdot1000\cdot79\cdot1000
21⋅1000⋅79⋅1000…
M
L
E
MLE
MLE等着你
2.2 正解
别搞什么花里胡哨的转化了,直接正面思考
定义
f
i
,
j
f_{i,j}
fi,j为满足
O
2
≥
i
,
N
2
≥
j
O_2\ge i,N_2\ge j
O2≥i,N2≥j的最小重量
那么转移方程显然:
f
i
,
j
=
m
i
n
{
f
i
,
j
,
f
m
a
x
{
i
−
o
2
k
,
0
}
,
m
a
x
{
j
−
n
2
k
,
0
}
+
w
k
}
f_{i,j}=min\{f_{i,j},f_{max\{i-o2_k,0\},max\{j-n2_k,0\}}+w_k\}
fi,j=min{fi,j,fmax{i−o2k,0},max{j−n2k,0}+wk}(
m
a
x
max
max是为了避免下标为负)
两个
f
o
r
for
for枚举
i
,
j
i,j
i,j顺序随便(此处为01背包,反向枚举)
对于
k
k
k直接读一个处理一个(代码中下标有些不一样)
注意初始化memset(f,127,sizeof(f);f[0][0]=0;
3 代码
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void Read(T &n){
char ch;bool flag=0;
while(!isdigit(ch=getchar()))if(ch=='-')flag=1;
for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
if(flag)n=-n;
}
int f[30][80];
int n, O2, N2, o2, n2, w;
int main(){
Read(O2); Read(N2); Read(n);
memset(f,127,sizeof(f));
f[0][0] = 0;
for(register int i=1; i<=n; i++){
Read(o2); Read(n2); Read(w);
for(register int j=O2; j>=0; j--)
for(register int k=N2; k>=0; k--)
f[j][k] = min(f[max(j-o2,0)][max(k-n2,0)]+w,f[j][k]);
}
cout<<f[O2][N2]<<endl;
return 0;
}