算法学习 - 01背包问题(动态规划C++)
- 动态规划
- 01背包
- 问题描述
- 求解思路
- 代码实现
- 放入哪些物品
- 代码
动态规划
我在上一篇博客里已经讲了一点动态规划了,传送门:算法学习 - 动态规划(DP问题)(C++)
这里说一下,遇到动态规划应该如何去想,才能找到解决办法。
最主要的其实是要找状态转移的方程,例如上一篇博客里面,找的就是当前两条生产线的第i个station的最短时间和上一时刻的时间关系。
minTime(station[1][i]) = minTime(station[1][i-1] + time[i], station[2][i-1] + time[i] + trans[i-1])
今天要讲的问题是01背包问题。
01背包
问题描述
01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2……Wn,与之相对应的价值为P1,P2……Pn。
求解这个背包能装的最大价值。(物体不能分割)。
求解思路
这个其实比上一道题难在哪里了呢?
难在上一题装配线始终是两个,不会多,不会少。我们变化的量只有装配站的多少。
这个题目变化的量一个是物品的数量,还有一个是背包的空间。就是说装配线不会随着station的多少而变化,但是背包的空间会随着物品的装入而变化。
所以我们需要做的是判断当背包剩余的空间为j
的时候,能否装入第i
个物品,并且总价值增加。
即:
d[j] = max ( d[j], d[j-v[i]] + w[i]); //v[i]表示第i个物品的体积,w[i]表示第i个物品的价值。
其实我最开始看到上面这个状态转移的方程的时候,觉得这不是肯定的在空间j
的时候放入物品,价值大于不放入物品的。毕竟只要放入就证明增加了价值啊。
其实不是这个样子的,因为只有在最开始都没有装入的时候,所有的都是0. 装入就一定价值增加。但是假如我们的物品如下:
物品1: 空间 2 价值 5
物品2: 空间 4 价值 3
当我们放入第一件物品的时候,假设背包空间是5,那么d[2…5]都是为 5. 因为d[0…1]空间不够所以为0。 当我们放入物品2的时候,d[5] = 5 > d[5 - 4] + 3
因为d[1] = 0;
。
发现了没有! 并不是放入就一定总价值增加!
所以我们遍历所有物品,从第一个物品开始,找当空间为j
的时候,装入物品是否会增加价值!
代码实现
代码其实行数很少,不好看那些写了两个屏幕的,可能并没有更多的功能。
//
// main.cpp
// DP_01backpack
//
// Created by Alps on 15/4/28.
// Copyright (c) 2015年 chen. All rights reserved.
//
// 代码中直接定义了石头的数量和背包的空间,其实可以不用提前定义。
// 这里为了方便,而且在C++ 11中可以动态规定数组大小。#include <iostream>
using namespace std;#ifndef STONE_NUM
#define STONE_NUM 5 //定义石头数量
#endif#ifndef BACKPACK_SPACE
#define BACKPACK_SPACE 10
#endifint main(int argc, const char * argv[]) {int stoneSpace, stoneValue;//保存当前输入的物品空间和价值int value[BACKPACK_SPACE+1] = {0}; //初始化value数组for (int i = 0; i < STONE_NUM; i++) {scanf("%d %d", &stoneSpace, &stoneValue);//接收物品输入for (int j = BACKPACK_SPACE; j > 0; j--) {//当背包空间在不同的时候,** j 一定是从大到小**if (j >= stoneSpace && value[j] < (value[j-stoneSpace] + stoneValue)) {//假如背包空间足够,并且放入总价值增加value[j] = value[j-stoneSpace] + stoneValue;//放入,并更新总价值}}}printf("%d\n", value[BACKPACK_SPACE]);return 0;
}
这里解释下,为何第二层for
循环里,j 是从最大变化到最小的。因为我们比较的是当前物品放入,价值是否有变化,就是 value[j] 和 value[j - v] 比较。 假如更新了,那么 value[j + 1] 需要和 value[j + 1 - v] 比较的值,可能 value[j + 1 - v]已经被更新过了。就不行了!因为那个时候value[j + 1 -v]的总价值已经算上当前的物品了。再算就重复了。
放入哪些物品
上面的代码没有知道到底放入了哪些物品,其实是因为为了节省空间,没有保存每个 stoneSpace和 stoneValue.
所以我们把它们变成数组就好了。
然后我们首先要做的是:找到一共背包用了多少空间!
这个很重要,因为背包的最大价值不一定是装满了,所以我们要找到用了多少空间,才能知道到底放入了哪些物品。
怎么找呢?
假如我们的value数组如下:
0
0
4
6
9
10
13
15
15
19
19
很容易知道,这个背包剩余了一个空间? 为什么呢,因为最大的19有两个,j 多了1但是价值没有增加,说明这 1的空间没有放入物品,也就是空余了。
这样找到第一个最大得数的下标,就是使用的空间了。
代码
比较简单,只不过多了一步查找那些物品放入了,就是当背包空间减去物品空间,总价值也刚好增加了物品的价值数量,那么说明这个物品被放入了。
代码如下:
//
// main.cpp
// DP_01backpack
//
// Created by Alps on 15/4/28.
// Copyright (c) 2015年 chen. All rights reserved.
//
// 代码中直接定义了石头的数量和背包的空间,其实可以不用提前定义。
// 这里为了方便,而且在C++ 11中可以动态规定数组大小。#include <iostream>
using namespace std;#ifndef STONE_NUM
#define STONE_NUM 5 //定义石头数量
#endif#ifndef BACKPACK_SPACE
#define BACKPACK_SPACE 10
#endifint main(int argc, const char * argv[]) {int value[BACKPACK_SPACE+1] = {0};int stoneSpace[STONE_NUM],stoneValue[STONE_NUM];for (int i = 0; i < STONE_NUM; i++) {scanf("%d %d", &stoneSpace[i], &stoneValue[i]);for (int j = BACKPACK_SPACE; j > 0; j--) {if (j >= stoneSpace[i] && value[j] < value[j-stoneSpace[i]] + stoneValue[i]) {value[j] = value[j-stoneSpace[i]] + stoneValue[i];}}}//以上代码几乎无变化,只是存储是数组存储了。for (int i = 0; i <= BACKPACK_SPACE; i++) {printf("%d\n", value[i]);}//打印value数组,这里面存放的是,所有物品在背包只有空间i的时候,达到的最大总价值。int backPackSpace = BACKPACK_SPACE;// 得到背包空间while (value[backPackSpace] == value[backPackSpace-1]) {backPackSpace--;//假如背包价值和背包空间-1的时候价值相同,空间 -1}//找到一共使用了多少空间for (int i = STONE_NUM-1; i >= 0; i--) {if ((backPackSpace-stoneSpace[i] >= 0) && value[backPackSpace] == value[backPackSpace-stoneSpace[i]] + stoneValue[i]) { //假如减去当前物品的空间,总价值刚好和物品的价值相等,说明此物品被放入了。printf("%d ", i+1);//打印这个物品。i+1是因为物品的下标是从0开始的。backPackSpace = backPackSpace - stoneSpace[i]; //背包放入了这个物品,自然空间减少了。}}return 0;
}
整个代码比较简单。 有疑问的留言,可以相互交流。
我看了一篇其他博客,用二维矩阵入门,也觉得很棒。推荐:http://www.hawstein.com/posts/dp-knapsack.html
算法学习 - 01背包问题(动态规划C++)相关推荐
- vs2017\vs2019 动态规划算法实现0-1背包问题 C
这是针对于博客vs2017安装和使用教程(详细)和vs2019安装和使用教程(详细)的动态规划算法实现0-1背包问题的示例 目录 一.问题描述
- 01背包问题 动态规划 java实现 简单通俗易懂
** 01背包问题 动态规划 ** 1.动态规划 什么是动态规划?动态规划就是将一个大问题不断向下拆分成小问题,直到拆分出的小问题可以求出其解,然后将小问题的解不断的向上合并,最终得到大问题的解决方案 ...
- 算法设计 - 01背包问题的状态转移方程优化,以及完全背包问题
01背包问题的一维状态转移方程的推导 前提摘要 前面这篇博客中: 算法设计 - 01背包问题_伏城之外的博客-CSDN博客 我们已经推导出了01背包问题的二维数组dp状态转移方程公式: 假设有N种不同 ...
- 基于改进遗传退火算法的0-1背包问题设计与实现
JISHOUUNIVERSITY 本科生毕业设计 题 目: 基于改进遗传退火算法的0-1背包问题设计与实现 作 者: 秦峰 学 号: 20144042001 所属学院: 信息科学与工程 ...
- 背包问题动态规划matlab,01背包问题动态规划详解
计算机算法分析考试:动态规划0-1背包问题,怎么算她说她没醉,却一直摇摇晃晃掉眼泪:你说你爱她,却从未想过给她一个家. 要考试了,老师给划重点有一题:动态规划0-1背包问题,怎么算. 怎么理问题描述: ...
- 分支界限算法【0-1背包问题】按照优先队列式(LC)分支限界法求解0-1背包问题, 并给出限界函数,并画出该实例的状态空间树。
目 录 回溯算法[0-1背包问题] 分支界限算法[0-1背包问题] 作业题(期末考试必考) 小结 回溯算法[0-1背包问题] 分支界限算法[0-1背包问题] 解决思路:采用优先队列式分支限界 Ø ...
- 01背包问题动态规划(二维数组)
01背包问题动态规划(二维数组) 问题描述 一个旅行者有一个最多能装 M 公斤的背包,现在有 n 件物品,它们的重量分别是W1,W2,-,Wn,它们的价值分别为C1,C2,-,Cn,求旅行者能获得 ...
- 0-1背包问题动态规划模型的Python解法
0-1背包问题动态规划模型的Python解法 1.01背包问题 2.Python解决方案 3.01背包问题例题 1.01背包问题 背包问题(Knapsack problem)是一种组合优化的NP完全问 ...
- 算法设计与分析 0-1背包问题 动态规划解法【超详细】
0-1背包问题 问题描述 给定i个物品和一个容量为的背包,物品的重量是Wi,其价值为Vi 物品个数为i,背包容量为C. 如何选择装入背包内的物品,使得装入背包中的物品的总价值最大? 其中,每种物品只有 ...
最新文章
- 周志华教授:关于深度学习的一点思考!
- 程承熊LEE微购店的买家秀
- linux shell 判断一个命令是否存在
- 强化学习(十六) 深度确定性策略梯度(DDPG)
- QGLViewer 编译安装步骤
- 【LeetCode】剑指 Offer 27. 二叉树的镜像
- js中Array数组基本方法
- Fusion Studio 17 for Mac(视频后期特效合成软件)
- 一周二次课(12月12日)
- c语言下建立直角坐标系,大地坐标系和地心地固直角坐标ECEF转换公式和C语言函数代码...
- JDK源码系列 下载源码
- 数组公式和普通公式,差别在于思维方式的不同! 要用看数组的方式看待数据
- 1013. Battle Over Cities (25)
- 2009年最新虚拟主机提供商排名榜
- 阿里VIP会员看腾讯视频?这不是一个意外的选择
- java 保存文件在服务器_Java中如何将数据保存到服务器端
- t检验 显著性检验 significance test
- 为视图或函数指定的列名比其定义中的列多
- 2100306-52-1,N-Mal-N-bis(PEG4-acid)一种马来酰亚胺PEG试剂,具有两个末端羧酸和一个马来酰亚胺(Mal)基团
- vue-cli创建vue2或vue3新项目步骤【留底收藏】
热门文章
- Wireshark --> 抓包(网络分析)工具
- 属性选择器 class^=
- java 80端口_Java80端口占用异常解决方法有什么?
- 某联招聘版某数反反爬— 2. 总体剖析
- Chrome - JavaScript调试技巧总结(浏览器调试JS)
- java飞机大战背景图片_Java简单飞机大战(1)
- 安徽省宣城市住房公积金管理中心的数据安全建设实践
- android11有哪种手机,哪些手机可以升级安卓11 Android11支持机型有哪些
- Vue - 项目中使用iconfont(阿里图标库)
- C语言(函数指针数组)详解