二维费用的背包问题 以 一本通1271潜水员 为例+规律总结

1271:【例9.15】潜水员

【题目描述】
潜水员为了潜水要使用特殊的装备。他有一个带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、代价2。在这里设第 i 件物品所需要的两种代价分别为w1[1],w2[i]。两种代价可以付出的最大值(两种背包的容量)分别为m1,m2.物体的价值为c[i]。
算法:费用加了一维,只需要状态增加一维即可。原本的二维数组,可以变成三维的。即 f[i][v][u] 表示考虑前 i 种物品付出代价1为v和代价2为u时能够获得的最大值。
这样以 01背包 为例,状态转移方程就变成了 f[i][v][u] = max( f[i-1][v - w1[i]][u - w2[i]] + c[i],f[i-1][v][u] )。
但是根据我们之前将二维数组压缩成一维数组的思路,我们可以将三维的数组压缩成二维的数组。只不过仍然要注意 " 01背包是从后向前遍历,无后效性;完全背包是从前向后遍历,无限次成倍再考虑"。
物品总个数的限制(一本通书本上提出的):
有时,“二维费用”的条件是以这样一种隐含的方式给出的,最多只能取M件物品,这实际上相当于每件物品多了一种“件数”的费用,每个物品的件数费用均为1,可以付出的最大件数费用为M。也就是f[v][m] 表示付出费用v、最多选m件时可以得到的最大价值。根据物品类型(01、完全、多重)用不同的方法循环更新,最后再f[0…V][0…M]范围内寻找答案。如果要求“恰好取M件物品”,就在f[0…V][M](定义一个维度)的范围内寻找答案。(根据要求选择范围,寻找答案)

本题分析:

潜水员这道题可以看做是二维费用的 01 背包问题。
1、确定价值和代价:所求即为价值——重量。 两种代价,一种代价为含氧量,另一种代价为含氮量。
2、确定寻找范围:题目对两种代价的要求是必须大于等于m1,m2。所以我们可以将大于m1的都放在m1中,将大于m2的都放在m2中,这样对结果没有任何影响。
3、初始化:最后需要求最小,所以一开始将整个二维数组都初始为较大的数。
4、标准的背包问题中,是所装物品的重量不能超过背包能够容纳的重量,所以遍历中是当前单元格是由前面的单元格确定。但是本题中,代价是最小限制,就是物品的对应的量的值必须要大于所给的代价,所以才是借助当前单元格确定后面的单元格。(注意第一个单元格a[0][0]初始化为0,这样才能将第一个物品放进去)

另:关于背包问题变式的一些总结:

  • 装到恰好:
    代价为0时初始化为0 其他无限小 —在求最大价值的时候。
    代价为0时初始化为1,其他都为0.—在求方案总数的时候。
    不用装到恰好:
    初始化全为0 —在求最大价值的时候
    而求方案种数的时候—(就相当于每个价值为1的,求不用装到恰好的求最大值)
  • 不能超过代价 前面的单元格确定此单元格
    必须超过代价 当前的单元格确定后面的单元格
  • 从后向前遍历无后效性,表示此物品只考虑一次
    从前向后遍历有后效性,表示此物品可以多次考虑无限次(在考虑过的基础上再考虑)
  • 取最值为最大值初始化为零。
    取最值为最小值,初始化为极大值
  • 在求方案种数时:1、初始化的时候需要注意(具体看上面)2、在求解的时候用sum 将符合条件的加起来。3、其他的分析是哪种背包,采用相应的方法。

本题代码:

#include
#include
#include
#include
using namespace std;
//二维费用的背包问题 一种物品有两种代价,和一个价值。
//需要按照要求,再考虑完第i中物品之后,找出符合条件的解 
//二维的 01背包 代价1:氧气 代价2:氮气  价值:所求结果——重量 求最小 
//本题的代价是最低限制,也就是说可以超过代价,但是所求的结果不能小于代价 
//之前的 01背包是由前面的数来确定a[j]当前这个单元格的位置,且不能超过代价。
//但是本题的01背包是由当前的单元格确定后面的单元格,而且可以超过代价,一旦超过代价就与等于了代价是一样的。 
int main()
{
     
 int m1,m2;//需要的最低氧氮量 
 cin>>m1>>m2;
 int n;
 cin>>n;//气缸的个数
 int a[22][80];//一维表示氧气 二维表示氮气 单元格内记录重量 
 int w1[1001];//代价1
 int w2[1001];//代价2
 int v[1001];//价值
 memset(a,127,sizeof(a));//因为结果是要求最小的价值(重量)所以初始化要初始化为一个很大的值 
 a[0][0]=0;//为了让第一个物品能够放进去这样就可以用a[0][0]确定a[w1[i]][w2[i]]单元格的数值了 
 for(int i=1;i<=n;i++)
 {
     
  cin>>w1[i]>>w2[i]>>v[i];
  } 
 
 //01背包 
 int h2,l2;
 for(int i=1;i<=n;i++)
 {
     
  //从后往前是为了无后效性  也就是对每个物品只考虑了一次 
  for(int h=m1;h>=0;h--)//行 氧气 因为可以超过代价,所以就算所需的氧气小于这个气缸的氧气也可以加上这个气缸 
  {
     
   for(int l=m2;l>=0;l--)//列 氮气
   {
     
    //根据a[h][l]单元格去确定之后的a[h+w1[i]][l+w2[i]]单元格的值
    h2=h+w1[i];
    l2=l+w2[i];
    if(h2>m1)//如果大于了需求,就直接用需求量代换,放在这种代价的最大需求格子中。因为只要满足了需求就是一样的 
     h2=m1;
    if(l2>m2)
     l2=m2;
    a[h2][l2]=min(a[h2][l2],a[h][l]+v[i]);
    } 
   } 
  
  //检测 
  /*for(int h=0;h<=m1;h++)
  {
   for(int l=0;l<=m2;l++)
   {
    printf("%10d ",a[h][l]);
   }
   cout<
 }
 
 cout<<a[m1][m2];
 return 0;
}

你可能感兴趣的:(信息学奥赛,代码,动态规划,算法,动态规划)