潜水员(需求类多维背包)(伪费用)
先上题目:
潜水员为了潜水要使用特殊的装备。他有一个带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(1≤m≤21,1≤n≤79)。它们表示氧,氮各自需要的量。
第二行为整数k(1≤n≤1000)表示气缸的个数。
此后的k行,每行包括ai,bi,ci(1≤ai≤21,1≤bi≤79,1≤ci≤800)3ai,bi,ci(1≤ai≤21,1≤bi≤79,1≤ci≤800)3整数。这些各自是:第i个气缸里的氧和氮的容量及汽缸重量。
输出:
仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。
样例:
输入:
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
输出
249
分析一下:
这道题有几个基本特点
(1)选取的是最小值,要用到min(如果不赋初值,就会全是0)
(2)这里的“费用“其实是需求,因此最终答案应满足需求,
也就是选取的气体总量可以超出需求量,
(因此上界是无法确定的,无法通过枚举之后的总气体量进行状态转移)
这个是解题过程(翻车锦集),正解在再下一块
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int main() { int k, m, n, dp[85][85] = {}, sav1[1005] = {}, sav2[1005] = {}, sav3[1005] = {}; scanf("%d%d", &m, &n); scanf("%d", &k); memset(dp,214711111,sizeof(dp)); dp[0][0] = 0; for(int i = 1;i <= k;i++) { scanf("%d%d%d",&sav1[i],&sav2[i],&sav3[i]); } /* for(int g = 1;g <= k;g++) { for(int i = m;i >= sav1[g];i--) { for(int j = n;j >= sav2[g];j--) { if(dp[i][j] && dp[i-sav1[g]][j-sav2[g]]+sav3[g]) { dp[i][j] = min(dp[i][j],dp[i-sav1[g]][j-sav2[g]]+sav3[g]); } else { if(dp[i][j]) { dp[i][j] = dp[i][j]; } if(dp[i][j] = dp[i-sav1[g]][j-sav2[g]]+sav3[g]) { dp[i][j] = dp[i][j] = dp[i-sav1[g]][j-sav2[g]]+sav3[g]; } } //dp[i][j] = min(dp[i][j],dp[i-sav1[g]][j-sav2[g]]+sav3[g]); } } }*/ for(int g = 1;g <= k;g++) { for(int i = m;i >= 0;i--) { for(int j = n;j>=0;j--)//枚举的不是选择当前气罐之后的已有量,枚举的是选择当前气罐之前的已有量 {//这里一定要从m-->0枚举(因为上界无法确定) int now1 = i + sav1[g]; int now2 = j + sav2[g]; if(now1 > m) now1 = m; if(now2 > n) now2 = n;//即使总量超过需求量,也可以选 //if(dp[a][b] > dp[i][j]+sav3[g]) dp[a][b] = dp[i][j]+sav3[g]; dp[now1][now2] = min(dp[now1][now2],dp[i][j]+sav3[g]); } } } printf("%d", dp[m][n]); return 0; } /* 5 60 5 3 36 120 10 25 129 5 50 250 1 45 130 4 20 119 */ /*特点 (1)根据实际意义,费用不是真“费用”,是要满足的需求,因此总量超过的需求量也可以 (2)要求取最小重量,因此dp数组里存的是最小值,转移要用到min。注意:如果用到min,就一定要memset给dp数组赋一个 大整数的初值,防止最后全是0 */
正解
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int main() { int k, m, n, dp[85][85] = {}, sav1[1005] = {}, sav2[1005] = {}, sav3[1005] = {}; scanf("%d%d", &m, &n); scanf("%d", &k); memset(dp,214711111,sizeof(dp)); dp[0][0] = 0; for(int i = 1;i <= k;i++) { scanf("%d%d%d",&sav1[i],&sav2[i],&sav3[i]); } for(int g = 1;g <= k;g++) { for(int i = m;i >= 0;i--) { for(int j = n;j>=0;j--)//枚举的不是选择当前气罐之后的已有量,枚举的是选择当前气罐之前的已有量 {//这里一定要从m-->0枚举(因为上界无法确定) int now1 = i + sav1[g]; int now2 = j + sav2[g]; if(now1 > m) now1 = m; if(now2 > n) now2 = n;//即使总量超过需求量,也可以选 dp[now1][now2] = min(dp[now1][now2],dp[i][j]+sav3[g]); } } } printf("%d", dp[m][n]); return 0; } /*特点 (1)根据实际意义,费用不是真“费用”,是要满足的需求,因此总量超过的需求量也可以 (2)要求取最小重量,因此dp数组里存的是最小值,转移要用到min。注意:如果用到min,就一定要memset给dp数组赋一个 大整数的初值,防止最后全是0 */