c/c++语言开发共享背包详解

背包问题是动态规划最基础的基础,这里本蒟蒻讲解一下几类背包问题。 01背包 题目 有N件物品和一个容量为V的背包,每种物品只有一个,放入第i件物品的费用是Ci,价值是Wi。 求将若干个物品装入背包得到的最大价值。(总费用不能超过总容量V) 解析 最基本的背包,每种物品只有一个,可放可不放。 很容易可 …

背包问题是动态规划最基础的基础,这里本蒟蒻讲解一下几类背包问题。

01背包

题目

有n件物品和一个容量为v的背包,每种物品只有一个,放入第i件物品的费用是ci,价值是wi

求将若干个物品装入背包得到的最大价值。(总费用不能超过总容量v)

解析

最基本的背包,每种物品只有一个,可放可不放。

很容易可以定义出状态f[i][j]表示前i件物品放入一个容量为j的背包的最大价值。

状态转移方程是f[i][j]=max(f[i-1][j],f[i-1][v-ci]+wi)(即不放与放)。

所以可以得到代码:

//f数组初值为0  for(int i=1;i<=n;i++)  {      for(int j=0;j<c[i];j++)          f[i][j]=f[i-1][j];      for(int j=c[i];j<=v;j++)          f[i][j]=max(f[i-1][j],f[i-1][j-c[i]]+w[i]);              }


很显然时间复杂度已将无法优化了,考虑优化空间复杂度。

第二维显然无法舍去,考虑舍去第一维。

定义状态f[j]表示原来的f[i][j],舍去的i将在循环中体现。

f[i][j]是由f[i-1][j]与f[i-1][j-ci]推导出的,所以我们需要保证f[j]也是由它们推导而出。

事实上,如果我们逆着做j循环,即j从v到0循环,计算f[j]时,f[j-ci]的值就是f[i-1][j-ci]的值。

所以我们可以将其优化成一维数组。


关于f数组初值的问题:

如果要求恰好装满背包,那么初始化时f[0]=0,其它均为负无穷,因为除了容量为0的背包可以在什么都不装时价格为0,其它容量的背包都不行(即不合法)。

如果没有要求恰好装满背包,那么初始化时f数组全部为0,因为任何容量的背包都可以不装任何物品价格为0。

code

//f数组初值为0  for(int i=1;i<=n;i++)      for(int j=v;j>=c[i];j--)          f[j]=max(f[j],f[j-c[i]]+w[i]

 

 

 

 

 

完全背包

题目

有n件物品和一个容量为v的背包,每种物品可以无限使用,放入第i件物品的费用是ci,价值是wi

求将若干个物品装入背包得到的最大价值。

解析

完全背包和01背包非常相似,唯一的区别就是01背包每种物品只能用一次,而完全背包可以无限使用。

所以考虑将完全背包转化成01背包,仔细想想,01背包在j循环时逆推是为了保证f[i][j]是由f[i-1][v-ci]推导而来,即每种物品只选一次。

而如果我们正推j循环,即从ci到v,那么每种物品都可以无限选用,这不正是完全背包吗?

code

//f数组初值均为0  for(int i=1;i<=n;i++)      for(int j=c[i];j<=v;j++)          f[j]=max(f[j],f[v-c[i]]+w[i])

 

 

 

 

 

多重背包

题目

有n件物品和一个容量为v的背包,每种物品有mi个,放入第i件物品的费用是ci,价值是wi

求将若干个物品装入背包得到的最大价值。

解析

依旧转化成01背包求解,把第i种物品转换成mi件只有1个的物品。

然后用求解01背包的方法来做即可。

code

int k=n;  for(int i=1;i<=n;i++)      while(m[i]>1)      {            c[++k]=c[i];            w[k]=w[i];            m[i]--;      }  //f数组初值均为0  for(int i=1;i<=k;i++)      for(int j=v;j>=c[i];j--)          f[j]=max(f[j],f[j-c[i]]+w[i])

 

 

 

 

 

例题一:开心的金明

题目

【题目描述】

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。

更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过n元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的n元。

于是,他把每件物品规定了一个重要度,分为5等:用整数15表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。

他希望在不超过n元(可以等于n元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,,jk,则所求的总和为:

v[j1]×w[j1]+v[j2]×w[j2]++v[jk]×w[jk]。

请你帮助金明设计一个满足要求的购物单。

【输入格式】

第一行,为2个正整数,用一个空格隔开:n m(其中n表示总钱数,m为希望购买物品的个数。)

从第2行到第m+1行,第j行给出了编号为j1的物品的基本数据,每行有2个非负整数v p(其中v表示该物品的价格vp表示该物品的重要度(15)

【输出格式】

1个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。

【输入样例】

1000 5  800 2  400 5  300 5  400 3  200 2

【输出样例】

3900

【数据规模】

n<30000,m<25,v≤10000

解析

裸的01背包,只需要注意价值是价格乘上重要度,再套上模板就可以了。

code

#include <algorithm>  #include <iostream>  #include <cstring>  #include <string>  #include <cstdio>  #include <cmath>  using namespace std;  int n,m,v[99999],p[99999],f[99999],maxn;  int main()  {      memset(f,0xcf,sizeof(f));      f[0]=0;      cin>>n>>m;      for(int i=1;i<=m;i++)       {          cin>>v[i]>>p[i];          p[i]*=v[i];      }      for(int i=1;i<=m;i++)          for(int j=n;j>=v[i];j--)          {              f[j]=max(f[j],f[j-v[i]]+p[i]);              maxn=max(maxn,f[j]);          }      cout<<maxn;      return 0;  }

 

 

 

 

 

例题二:金明的预算方案

详见本蒟蒻的博客

 

 

 

 

 

注:参考书籍:《背包九讲》

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/602149.html

(0)
上一篇 2021年5月11日
下一篇 2021年5月11日

精彩推荐