一、01背包(1)

问题描述

有n个重量和价值分别为wi和vi的物品,从这些物品中挑选出总价值不超过W的物品,求这些方案中价值最大的一种。

穷竭搜索

利用递归针对每种物品是否放入背包进行搜索,这种方法很容易想到,但是时间复杂度可能比较大,最坏的情况需要O(2n),当给出的n不太大时可以用。

void solve() {int rec(int i, int j);cout << rec(n - 1, W);
}
int rec(int i, int j) {//从第0到i这i+1个物品中选出总重量不超过jint res;if (i == -1)//没有物品可以挑选res = 0;else if (j < w[i])//无法挑选物品res = rec(i - 1, j);else//挑和不挑中选一个价值更大的res = max(rec(i - 1, j), rec(i - 1, j - w[i]) + v[i]);return  res;
}

由于上面朴素的算法时间复杂度太大,下面我们利用记忆化数组dp[i][j]对其进行优化,优化后的算法时间复杂度为O(nW),dp[i][j]表示从0到i这i+1个物品的总重量不超过j的物品的总价值最大者。这里注意我们要先对dp数组进行初始化memset(dp,-1,sizeof(dp))

void solve() {int rec(int i, int j);cout << rec(n - 1, W);
}
int rec(int i, int j) {if(dp[i][j] >= 0)//如果数组中已经存在,直接返回return dp[i][j];int res;if (i == -1) res = 0;else if (j < w[i])res = rec(i - 1, j);elseres = max(rec(i - 1, j), rec(i - 1, j - w[i]) + v[i]);return  dp[i][j] = res;//数组中不存在的存入数组
}

动态数组

上面已经提到过记忆化数组,下面我们进一步研究这个数组,我们可以直接根据递推关系填满这个数组,dp[i+1][j]表示从0到i这i+1个物品的总重量不超过j的物品的总价值最大者。而这样做的时间复杂度同样是O(nW)。

void solve() {for (int i = 0; i < n; i++) {for (int j = 0; j <= W; j++) {if (j < w[i])//无法挑选物品dp[i + 1][j] = dp[i][j];else//挑和不挑中选一个价值更大的dp[i + 1][j] = max(dp[i][j], dp[i][j - w[i]] + v[i]);   }}cout << dp[n][W] << endl;
}

状态转移

我们同样可以寻找每个状态和其之后状态的关系来解题,我们可以将“前i个物品中选取总重不超过j时的状态”转换成“前i+1个物品中选取总重不超过j时的状态”和“前i+1个物品中选取总重不超过j+w[i]时的状态“。

void solve() {for (int i = 0; i < n; i++) {for (int j = 0; j <= W; j++) {dp[i + 1][j] = max(dp[i + 1][j], dp[i][j]);//转换成上述第一种状态if(j + w[i] <= W)//转换成上述第二种状态dp[i + 1][j + w[i]] = max(dp[i + 1][j + w[i]],dp[i][j] + v[i]);}}cout << dp[n][W] << endl;
}

二、01背包(2)

问题描述

问题与第一个一样,只是上述三种解法是针对n和W较小时的,一旦n和W变得很大,即使0(nW)的复杂度也是很大的。

改变dp对象

我们发现之前dp数组是针对重量的,我们可以改变dp的对象,换成针对不同价值j求最小的重量。dp[i+1][j]表示前i个物品中挑选出价值总和不超过时总重量的最小值。

void solve() {fill(dp[0], dp[0] + 10001, 10000);//对第一行初始化为充分大的数10000dp[0][0] = 0;//前0个物品什么都不能挑选for (int i = 0; i < n; i++) {for (int j = 0; j <= 10000; j++) {if (j < v[i])dp[i + 1][j] = dp[i][j];elsedp[i + 1][j] = min(dp[i][j], dp[i][j - v[i]] + w[i]);}}int res = 0;for (int i = 0; i <= 10000; i++)if (dp[n][i] <= W)//小于总重的最大价值res = i;cout << res << endl;
}

三、完全背包

问题描述

问题与第一个类似,只是每个物品可以被多次挑选。

区别

01背包问题每个物品只能被挑选一次,所以每次需要讨论的对象是下一个,而完全背包问题每个物品可以被挑选多次,所以每次下一个讨论的对象还是本身。

void solve() {for (int i = 0; i < n; i++) {for (int j = 0; j <= W; j++) {if (j < w[i])dp[i + 1][j] = dp[i][j];elsefor(int k = 1; k * w[i] <= j; k++)dp[i + 1][j] = max(dp[i + 1][j], dp[i][j - k * w[i]] + k * v[i]);   }}cout << dp[n][W] << endl;
}

我们还可以把上述的一个判断换成一条语句,让k从0开始。

void solve() {for (int i = 0; i < n; i++) {for (int j = 0; j <= W; j++) {for(int k = 0; k * w[i] <= j; k++)dp[i + 1][j] = max(dp[i + 1][j], dp[i][j - k * w[i]] + k * v[i]);   }}cout << dp[n][W] << endl;
}

但是这样就存在了一个三重循环,时间复杂度为O(nW2),我们发现,这个算法中存在很多的多余计算,比如在dp[i+1][j]中的第k个情况,其实已经在dp[i+1][j-w[i]]中第k-1个情况计算过了。

void solve() {for (int i = 0; i < n; i++) {for (int j = 0; j <= W; j++) {if (j < w[i])dp[i + 1][j] = dp[i][j];elsedp[i + 1][j] = max(dp[i][j], dp[i + 1][j - w[i]] + v[i]);   }}cout << dp[n][W] << endl;
}

四、空间节省

上面利用动态数组的算法都是利用的二维数组dp,其实我们会发现,利用递推关系时,每次需要改变值的数组都是在同一行的,所以我们可以不断的覆盖前一行,只利用以为数组便可以求解。

01背包

void solve() {for (int i = 0; i < n; i++) {for (int j = W; j >= w[i]; j--) {//从后往前循环dp[j] = max(dp[j], dp[j - w[i]] + v[i]);}}cout << dp[W];
}

完全背包

void solve() {for (int i = 0; i < n; i++) {for (int j = w[i]; j <= W; j++) {//从前往后循环dp[j] = max(dp[j], dp[j - w[i]] + v[i]);}}cout << dp[W];
}

需要注意的是两个问题的j的循环方向不同,前者需要从后往前循环,因为数组后面需要用到前面改变之前的值,而后者是针对的对象是自己。

背包问题(01背包和完全背包)相关推荐

  1. 背包问题教程-01背包,完全背包,多重背包,混合背包 收藏

    P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使价值总和最大. 基本思路 这是最基础的背包问题,特点是:每种物品仅有 ...

  2. 01背包、完全背包、多重背包问题的C++实现及路径记录

    这里主要实现路径记录,只求最值问题移步 01背包.完全背包.多重背包问题的C++实现 以下均打印输出路径,即装入背包的物品序号,和最大值. 01背包问题 #include <iostream&g ...

  3. 01背包、完全背包、多重背包问题的C++实现

    01背包问题 容量为10的背包,有5种物品,每种物品只有一个,其重量分别为5,4,3,2,1,其价值分别为1,2,3,4,5. 设计算法,实现背包内物品价值最大. 代码如下(输出14) #includ ...

  4. 01背包,完全背包,多重背包,混合背包,二维费用背包,分组背包,背包问题求方案数

    1 01背包问题 有 NNN 件物品和一个容量是 VVV 的背包.每件物品只能使用 一次. 第 iii 件物品的体积是 viv_ivi​,价值是 wiw_iwi​.求解将哪些物品装入背包,可使这些物品 ...

  5. 完全背包问题+01背包问题+分组背包+多重背包 总结

    背包问题都涉及到动态规划,利用dp进行更加优化的计算. 一.01背包 最基本的是01背包问题,题目一般类似:"在一定数目物品内,挑选总重量不超过一定数目的物品,其中每个物品只能选一次,求背包 ...

  6. 背包问题(01背包,完全背包,多重背包(朴素算法二进制优化))

    写在前面:我是一只蒟蒻~~~ 今天我们要讲讲动态规划中~~最最最最最~~~~简单~~的背包问题 1. 首先,我们先介绍一下  01背包 大家先看一下这道01背包的问题   题目   有m件物品和一个容 ...

  7. 背包问题——01背包

    背包问题--01背包 01背包作为动态规划(dynamic programing)中最基础的问题,需要我们彻底理解其中的原理,为以后解决更难的动态规划问题打下良好的基础. 这里拟定一个01背包问题: ...

  8. 动态规划之背包问题——01背包

    算法相关数据结构总结: 序号 数据结构 文章 1 动态规划 动态规划之背包问题--01背包 动态规划之背包问题--完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 ...

  9. C++ 背包问题——01背包

    由于编辑器原因,01背包文章搬家了,想看到更好的01背包问题题解,请点击链接: C++背包问题--01背包_小天狼星_布莱克的博客-CSDN博客

  10. 动态规划/背包问题总结/小结——01背包、完全背包

    文章目录 前言 动态规划五步法 01背包模型 完全背包 总结 前言 背包问题也属于动态规划问题. 动态规划就是将复杂的大问题转化为一个小问题,然后将小问题转化为更小的容易求解的问题:通过将最小的容易求 ...

最新文章

  1. 【ADO.NET】2、各种版本的 简单登录验证
  2. android 开发jni,示例:hello-jni
  3. 从IT人士到IT经理倪应该学会的30 项技能
  4. 《C#编程风格》还记得多少
  5. 用Twebbrowser做可控编辑器与MSHTML
  6. OCM备考 一、Server config 之网络配置
  7. SequoiaDB 系列之六 :源码分析之coord节点
  8. (学习)linux驱动学习知识积累(一)
  9. APT 组织将目标锁定Linux
  10. [C/C++] String Reverse 字符串 反转
  11. cisco2811路由器密码恢复
  12. oracle临时表空间可以删除吗,Oracle临时表空间的增删改查
  13. Hyperledger Fabric 1.0 公有云安装7--集群部署记录
  14. 【转】Java线程系列:Callable和Future
  15. python 语料_用python从语料库中提取最常用的词
  16. 【汇正财经】成交量的三种表达方式
  17. 十分感谢--致谢好友的
  18. java jcmd,JVM工具_jcmd
  19. unity 3d 原创制作射击游戏(一)
  20. HDU 6595 Everything Is Generated In Equal Probability (期望dp,线性推导)

热门文章

  1. 油罐车起火造成损失和伤亡?资产监测设备可远程监测油罐车运输!
  2. php preg_replace()漏洞记录
  3. 第四次作业——个人作业——必应词典软件案例分析
  4. gui学生信息管理系统java,Java实训·GUI学生信息管理系统
  5. FNDLOAD命令整合
  6. gitbook 使用粘自csdn
  7. 从C、C++、Java到Python,编程入门到底学什么语言好?
  8. python演奏音乐
  9. 杭州电子科技大学全国计算机排名,杭电排名为什么比211还高,杭州电子科技大学是211吗...
  10. win10 64位搭建汇编环境debug