首页 > 其他分享 >背包问题

背包问题

时间:2023-01-19 17:23:39浏览次数:28  
标签:背包 int max 问题 ++ vec items

背包问题


1. 0-1背包问题

0-1背包问题为:有N件物品和一个容量为V的背包,第i件物品的体积和价值分别为volume[i]和value[i],求解将哪些物品装入背包可以获得最大价值
针对0-1背包问题,每个物品都有放或者不放两个选择,并且放只能放一次,问题的状态可以表述为:
f[i][j]:表示前i件物品放入容积为j的背包的最大价值,其状态转移方程就可表述为:
f[i][j] = max(f[i-1][j], f[i-1][j-volume[i]]+value[i])
代码实现如下:

int find_max_value(vector<vector<int>>& items, int n, int v){
  int f[1005][1005] = {0};
  for(int i = 1 ; i <= n ; i++){
    for(int j = 0 ; j <= v ; j++){
	  f[i][j] = f[i-1][j];
	  if(j>= items[i-1][0]){
	    f[i][j] = max(f[i][j],f[i-1][j-items[i-1][0]] + items[i-1][1]);
	  }
    }
  }
  return f[n][v];
}

考虑代码优化:从代码中我们可以看到,转移方程中,每个变量只与上一个变量有关系,因此我们没有必要进行全部存储,可以通过使用一维数组的方法,为了保证变量刷新的正确性,采用从后往前刷的方式,确保已更新的数据对未更新的数据不产生影响。
代码实现如下:

int find_max_value(vector<vector<int>>& items , int n , int v){
  int f[1005] = {0};
  for(int i = 0 ; i < n; i++){
    for(int j = v ; j - items[i][0] >=0 ; j--)
      f[j] = max(f[j],f[j-items[i][0]]+items[i][1]);
  }
  return f[v];
}

2. 完全背包问题

完全背包问题的描述类似于0-1背包问题,其唯一的不同为:完全背包问题中的物品可以重复选择。最简单的思路为分别测试每个物品装若干个,直到体积不够为止。
具体代码如下所示:

int find_max_value(vector<vector<int>>&items, int n , int v){
  int f[1005] = {0};
  for(int i = 0 ; i < items.size(); i++){
    for(int j = items[i][0]; j <= v; j++){
      for(int k = 1 ; j - k * items[i][0] >= 0 ; k++)
        f[j] = max(f[j],f[j-k*items[i][0]]+k*items[i][1]);
    }
  }
  return f[v];
}

代码优化:上述代码可以转为如下的优化代码:

int find_max_value(vector<vector<int>>&items, int n , int v){
  int f[1005] = {0};
  for(int i = 0 ; i < items.size(); i++){
    for(int j = items[i][0]; j <= v; j++){
        f[j] = max(f[j],f[j-items[i][0]]+items[i][1]);
    }
  }
  return f[v];
}

具体优化原理为,当体积不断上升的时候,需要的状态在之前已经计算过了
规律:从01背包和完全背包可以看出,代码的不同只有在二次循环中,01背包从大到小刷新,完全背包从小到大刷新


3. 多重背包问题

多重背包问题和0-1背包问题相比,每个物品有一个可挑选的上限,最简单的思路为对于每一个物品,重复放入挑选上限次,这样就转化为01背包问题,例如物品A的挑选上线为4,就在物品池中放入四个A。
具体代码如下所示:

int find_max_value(vector<vector<int>>&items, int n , int v){
  int f[1005] = {0};
  vector<pair<int,int>>vec;
  for(int i = 0 ; i < items.size(); i++){
    for(int j = 0 ; j < items[i][2]; j++){
      vec.push_back({items[i][0],items[i][1]});
    }
  } //通过此步操作转为01背包问题
  for(int i = 0 ; i < vec.size(); i++){
    for(int j = v; j >= vec[i].first ; j--){
      f[j] = max(f[j],f[j-vec[i].first]+vec[i].second);
    }
  }
  return f[v];
}

代码优化:分析上述代码中存在的问题,我们发现将一个数字分解为若干个1的方法的复杂度过高,于是我们想到使用二进制的方法进行优化,例如 数字7 可以拆分为 1 , 2 , 4 。通过这三个数字的组合我们可以得到1-7的所有数字,10 可以拆分为 1, 2 , 4 , 3。通过这四个数字的任意组合我们可以得到1-10的所有数字,于是在第一步代码拆分的地方可替换为下面的二进制拆分法。

for(int i = 0 ; i < items.size(); i++){
  for(int k = 1 ; k <= items[i][2]; k*=2){
    items[i][2] -= k;
      vec.push_back({k*items[i][0],k*items[i][1]});
  }
  if(items[i][2] > 0)
      vec.push_back({items[i][2]*items[i][0],items[i][2]*items[i][1]});
}

其他代码保持不变


4. 混合背包问题

混合背包问题其实就是前面的几种背包问题的混合,有的物品只能选用一次,有的物品可以选用无数次,有的物品只能选用有限次,针对不同类别的物品采用各自的更新方法即可。
具体代码如下所示:

struct myitem{
  int vol;
  int val;
  int num;
}
int find_max_value(vector<vector<int>>&items,int n,int v){
  int f[1010] = {0};
  vector<myitem>vec;
  for(int i = 0 ; i < items.size(); i++){
    if(items[i][2] == -1)
      vec.push_bakc({items[i][0],items[i][1],items[i][2]});
    if(items[i][2] == 0)
      vec.push_back({items[i][0],items[i][1],items[i][2]});
    if(items[i][2] > 0){
      for(int k = 1; k <= items[i][2]; k *= 2){
         items[i][2] -=k;
         vec.push_back({items[i][0]*k,items[i][1]*k,-1});
       }
      if(items[i][2] > 0)
        vec.push_back({items[i][0]*items[i][2],items[i][1]*items[i][2],-1});
     }
  }
  for(int i = 0 ; i < vec.size();i++){
    if(vec[i].num != 0)
      for(int j = v; j >= vec[i].vol; j--)
         f[j] = max(f[j],f[j-vec[i].vol]+vec[i].val);
    else
      for(int j = vec[i].vol; j<= v; j++)
        f[j] = max(f[j],f[j-vec[i].vol]+vec[i].val);
  }
  return f[v];
}

5. 二维费用的背包问题

二维费用的背包问题在一维费用的背包问题上增加了一个约束条件,比如对背包的承重进行限制等
具体代码如下所示:

int find_max_val(vector<vector<int>>&items,int n,int v,int w){
  int f[1010][1010] = {0};
  for(int i = 0 ; i < n ; i++){
    for(int j = w ; j >= items[i][0]; j--){
      for(int k = v; k >= items[i][1]; k--)
        f[j][k] = max(f[j][k],f[j-items[i][0]][k-items[i][1]]+items[i][2]);
    }
  }
  return f[w][v];
}

6. 分组背包问题

分组背包问题相较于01背包问题增加了一个约束条件是,组内元素只能挑选一个,因此很直觉的思路为分别测试组内每一个物品带来的价值
具体代码如下所示

int find_max_val(unordered_set<vector<vector<int>>>&items,int v){
  int f[110] = {0};
  for(auto & item : items){
    for(int j = v ; j >= 0 ; j--)
      for(int k = 0 ; k < item.size(); k++)
        if(j-item[k][0] >= 0)
          f[j] = max(f[j],f[j-item[k][0]]+item[k][1]);
  }
  return f[v];
}

标签:背包,int,max,问题,++,vec,items
From: https://www.cnblogs.com/hhyandcpp/p/17061645.html

相关文章