01背包问题:

例题:传送门
01背包问题的特点:背包容量有限,物品只有一个,具有确定的体积和价值,我们的目标就是在不超过背包最大体积的情况下装入价值尽可能大的物品,让我们输出最大总价值

对于背包问题我们可以采用类似的思考方式:

以此为:

  1. 状态表示 考虑所有不同情况的结果存储方式
  2. 用集合表示背包问题的所有不同选法 f [ i ][ j ] 表示从0 ~ i 这些物品中选,最大背包容量为 j 的最大价值
  3. 然后考虑集合选法的条件, 数量 i 小于等于 物品总数 , 体积 j 小于等于 背包最大体积 V
  4. 至于背包问题的属性有已知几个情况:最大值,最小值, 数量
  5. 状态计算: 本质就是考虑如何根据上面的条件限制进行合理的集合划分 , 最终根据集合划分 进行状态转移方程的推导。

01 背包问题实例:

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8

我们首先用二维数组的方式来表示背包集合的状态:
对于f [ i ] [ j ] 的值我们可以从第 i 个物品选不选入手,如果第 i 个物品不选那么 f [ i ] [ j ] = f [ i -1 ] [ j ] 即等于上一层的最大价值 即从0~ i-1 个物品中选 且背包容量不超过 j 的最大值 , 如果在保证不超过背包容量 j 来选第 i 个物品 那么 f [ i ] [ j ] = f [ i -1 ] [ j - v [ i ] +w [ i ]这样我们就得到了容量不超过 j 而且海选则了 第 i 件物品的情况, f [ i ] [ j ] = max ( f [ i -1 ] [ j ], f [ i -1 ] [ j - v [ i ] + w[ i ])用循环依次进行这样的操作就得到了所有的状态下的最大价值,最后 f [ n ] [ V ] (n代表 n 个物品 数组的下标从0~ n ,V是背包最大容量) 就是最大价值。

01 背包代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N=1010;
int n,V;
int v[N];
int w[N];
int dp[N][N];
int maxn;
int main(){cin>>n>>V;for(int i=1;i<=n;i++){scanf("%d%d",&v[i],&w[i]);}for(int i=1;i<=n;i++){for(int vj=1;vj<=V;vj++){if(vj<v[i]) dp[i][vj]=dp[i-1][vj];else dp[i][vj]=max(dp[i-1][vj],dp[i-1][vj-v[i]]+w[i]);}}cout<<dp[n][V]<<endl;return 0;
}

时空复杂度分析和优化:

时间复杂度为O ( n^2 )主要体现在得到所有状态的过程,这个目前没有更好的优化方式,空间复杂度为 O( n^2 ) 主要体现在我们用一个二维数组存储所有的状态的最大价值,但是通过观察代码我们可以发现每个 dp [ i ] [ j ] 只与 i -1 层的 dp [ i -1] [ j ] 和它之前的 dp [ i- v [ i ] ] [ j ] 有关所以我们没有必要对于i-1 层之前的数值进行存储 , 我们可以将空间缩小到一维数组, 但是要注意的是如果用一维数组(滚动数组方式)存储第二层的循环 将要逆序循环, 这是因为:如果正序循环,等到循环到 j 时 dp[ j ]用的是i层的数据,这是不符合我们的思路的,当逆序循环是用的就是i-1 层的了。
下面是优化后的代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N=1010;
int n,V;
int v[N];
int w[N];
int dp[N];
int maxn;
int main(){cin>>n>>V;for(int i=1;i<=n;i++){scanf("%d%d",&v[i],&w[i]);}for(int i=1;i<=n;i++){for(int j=V;j>=v[i];j--){ // 逆序循环 保证dp[j]用到的是上一层的数据dp[j]=max(dp[j],dp[j-v[i]]+w[i]);}}cout<<dp[V]<<endl;return 0;
}

完全背包问题:

例题:传送门
01背包问题的升级版:每种物品有无限个了,而不是01背包问题中的一个,其他说明和01背包相同。

我们依然用闫式dp分析法按照步骤求解,
状态表示:集合 f[ i ][ j ] 表示从第1~i个物品中选 在背包容量不超过j的情况下物品的总最大价值
属性:最大价值
状态划分:可以将f[ i ][ j ]划分为好多种状态,即根据第i个物品选几个。
f[ i ][ j ]=max(f[ i -1 ][ j ], f[ i -1][ j - v[i]]+w[i]…,f[i-1][j-s*v[i]+w[i]]…); 1
f [ i ] [ j - v [ i ] ]= max(f[ i-1 ][ j ], f[ i - 1 ] [ j-v[i]] *w[i] …); 2

我们可以用2 式子加上w【i】从第一式的第二项开始替换
从而得到一个比较普遍的式子,
然后我们通过观察对比,这个式子和01背包问题的式子很像,所以可以用滚动数组来优化空间,
由于01 背包每一项用的是上一层(i-1) 的数值,但是完全背包问题由下标可得,是由本层中的前几项获得,所以
状态转移公式为:

//注意v要正序循环
f[j]=max(f[j],f[j-v[i]]+w[i]);

Code:

#include <iostream>
#include <stdio.h>
#include <vector>
#include <cmath>
#include <algorithm>
#include <map>
#include <string.h>
using namespace std;
const int maxn=1010;
int w[maxn],v[maxn];
int n,m;
int f[maxn];
int main(){cin>>n>>m;for(int i=1;i<=n;i++){scanf("%d%d",&v[i],&w[i]);}for(int i=1;i<=n;i++){for(int j=v[i];j<=m;j++){f[j]=max(f[j],f[j-v[i]]+w[i]);}}cout<<f[m]<<endl;
}

时空复杂度分析:

时间复杂度是O(n^2)空见复杂度为 O(n)n为10^4时都是可以接受的

多重背包问题:

例题:传送门
多重背包问题就是每种物品由多个,不是无限也不是一个,我们可以把它转化为01 背包问题做,思路是:将多个物品拆分成一个一个的,当然这样一定有点麻烦,第二种想法是在数据量小的时候:加一层循环从0循环到s【i】(我们用s【i】来存储第i种物品的个数);
然后套01背包的代码就可以了

Code:


#include <iostream>
#include <stdio.h>
#include <vector>
#include <cmath>
#include <algorithm>
#include <map>
#include <string.h>
using namespace std;
const int maxn=110;
int w[maxn],v[maxn],s[maxn];
int n,m;
int f[maxn];
int main(){cin>>n>>m;for(int i=1;i<=n;i++){scanf("%d%d%d",&v[i],&w[i],&s[i]);}for(int i=1;i<=n;i++){for(int j=m;j>=v[i];j--){for(int k=0;k<=s[i] && k*v[i]<=j;k++){f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);}}}cout<<f[m]<<endl;
}

时空复杂度分析:

时间复杂度O( n ^3) 空间复杂度O( n ) 时间复杂度确实不小,只能承受n不超过100的情况哦

二进制优化降低时间复杂度:

例题:传送门
这道题的数据就加强了,N最坏情况为1000,V是2000,v,w,s都不超过2000,显然用上面的直接暴力做法一定超时,
这里介绍一种二进制的优化方式,降低时间复杂度
对于大数n从1到n进行枚举的这种情况优化明显。
思路:
对于每一个s[ i ] ,从1~ s[ i ] 可以用用几位二进制数进行表示,当表示的最大二进制数小于s[ i ] 时 最后可以用s[ i ]- 最大二进制数表示最后一项数字。
这样我们就把一个大数分成了多组小的数字,用这写小的数字的组合可以组成1~ s[ i ]的任何一个数字。

Code:

#include <iostream>
#include <stdio.h>
#include <vector>
#include <cmath>
#include <algorithm>
#include <map>
#include <string.h>
using namespace std;
const int maxn=12010;
int w[maxn],v[maxn],s;
int n,V;
int a,b;
int f[maxn];
int main(){int cnt=1;cin>>n>>V;for(int i=1;i<=n;i++){scanf("%d%d%d",&a,&b,&s);int m=1;while(m<=s){s-=m;v[cnt]=m*a,w[cnt++]=m*b;//分成用每次乘二表示的数打包成一件物品成一项加入数组m*=2;}if(s>0){v[cnt]=s*a,w[cnt++]=s*b;//剩下的单成以项,加入数组}}for(int i=1;i<cnt;i++){for(int j=V;j>=v[i];j--){f[j]=max(f[j],f[j-v[i]]+w[i]);}}cout<<f[V]<<endl;
}

时空复杂度分析:

时间复杂度O( n^2logn) 在n小于1000时是可以接受的
空间复杂度为O(n)

分组背包问题:

概念:分组背包问题就是,在每一物品组中只选择一个物品
例题:传送门

Code:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N=110;
const int maxn=24010;
int dp[N];
int n,V;
int s[N],v[N][N],w[N][N];
int main(){cin>>n>>V;for(int i=0;i<n;i++){cin>>s[i];for(int j=0;j<s[i];j++){cin>>v[i][j]>>w[i][j];}}for(int i=0;i<n;i++){for(int j=V;j>=0;j--){for(int k=0;k<s[i];k++){if(j>=v[i][k]) dp[j]=max(dp[j],dp[j-v[i][k]]+w[i][k]);}}}cout<<dp[V]<<endl;
}

01背包问题,多重背包问题-分组背包问题-完全背包问题-总结-内含4种经典背包问题相关推荐

  1. 【动态规划】三种基本背包问题

    动态规划 是对解最优化问题的一种途径 它往往是针对一种最优化问题 根据问题的不同性质 确定不同的设计方法  因为这篇文章我想说点关于背包问题的事情 所以不再过多介绍动态规划  背包问题 是动态规划中的 ...

  2. 经典背包问题3——背包问题求方案数 、背包问题求具体方案

    经典背包问题3--背包问题求方案数 .背包问题求具体方案 1. 背包问题求方案数 2. 背包问题求具体方案 1. 背包问题求方案数 有 N 件物品和一个容量是 V的背包.每件物品只能使用一次. 第 i ...

  3. 四种常见背包问题整理

    四种常见背包问题整理 四种常见背包问题包括:① 最优装配 ② 部分背包问题 ③ 01背包问题 ④ 完全背包问题 ① 最优装配 给出 n 个物体,重量分别为 wi,使总重量不超过容量 C 的情况下选择尽 ...

  4. 动态规划之背包问题(01背包问题、完全背包问题、多重背包问题 I、多重背包问题 II 、分组背包问题)

    这里是目录

  5. 背包问题【01 完全 多重】

    目录 0-1背包 空间优化:二维变一维 完全背包 优化: 多重背包 要求恰好装满背包,那么在初始化时除了f[0]为0,其它f[1..V]均设为-∞ 如果并没有要求必须把背包装满,而是只希望价格尽量大, ...

  6. 经典背包问题 01背包+完全背包+多重背包

    01 背包 有n 种不同的物品,每个物品有两个属性,size 体积,value 价值,现在给一个容量为 w 的背包,问最多可带走多少价值的物品. int f[w+1]; //f[x] 表示背包容量为x ...

  7. 01背包问题+完全背包问题+多重背包问题

    一 01背包问题 1.1题目 有N件物品和一个容量为V 的背包.放入第i件物品耗费的空间是Ci,得到 的价值是Wi. 求解将哪些物品装入背包可使价值总和最大. 1.2 基本思路 这是最基础的背包问题, ...

  8. C++ 背包问题——多重背包

    C++背包问题好久没更新了,过来讲一讲背包问题中的多重背包: 1.多重背包定义: 要想了解多重背包,首先要了解01背包(会的也看一看),多重背包就是在01背包的基础上,增加了物品的个数,这一点要区别于 ...

  9. 瞎说一波3种基本背包问题【希望巨巨们指出错误】

    0/1背包: 这是自己接触最早的背包,其实说0和1背包是最早. 0和1背包: 他很自由,价值和重量成比例,像一块豆腐你想对某一块拿多少就切多少,那么很明显处理方案就是按照物品价值和重量的比值排序,也可 ...

最新文章

  1. 如何判断CPU、内存、磁盘的性能瓶颈?
  2. Entity Framework Code First关系映射约定
  3. 语音识别基础,总有一天你会用到
  4. Android应用Push至设备system目录
  5. 时光机穿梭---删除文件
  6. 没有IF-ELSE的工厂
  7. rust vs java_为什么我从Java切换到Rust
  8. python正则表达式 多个条件的匹配
  9. Chapter13:IPv6
  10. ixgbe驱动不支持三方兼容光模块SFP+SFP+或者QSFP的解决方案
  11. python lasso回归分析_解析python实现Lasso回归
  12. 计算机主板常见故障检修,主板常见故障检查与维修
  13. android tv outofmem,java.lang.OutOfMemoryError:尝试抛出OutOfMemoryErr...
  14. 假期之不务正业—— Qt+FFmpeg+百度api进行视频的语音识别
  15. AcWing 135. 最大子序和(单调队列优化 dp)
  16. eh怎么搜索重口sm_wifi怎么隐藏?
  17. 基于全志a33-vstar开发板的ap6210WiFi模块移植
  18. Linux shell、内核及系统编程精品资料下载汇总 topsage
  19. VisualBox解决CentOS中yum安装失败的问题
  20. js全屏操作之判断全屏

热门文章

  1. cad套索选择lisp_怎么将CAD2015,CAD2016的窗交窗口选择框的套索改为矩形吗
  2. 无营业执照开通微信商家码0.38%费率商户自助提交步骤
  3. 大货跟踪程序精简版v1.20200731
  4. 离一线城市越来越远?对不起,杭州没那么不堪
  5. [GO] Gin入门
  6. DDR扫盲——DDR与DDR2、DDR3的区别
  7. ddr4 lpddr4区别_笔记本内存LPDDR3就一定不如DDR4吗?宏旺半导体解释两者的区别?...
  8. vue3+ts+element-plus动态图标生成方式
  9. 全开源即时通讯(IM)系统-仿微信
  10. Week8 作业 C - 班长竞选 SCC Kosaraju HDU - 3639