从01背包问题理解动态规划
2016-04-01 16:17
211 查看
原文地址:http://www.cnblogs.com/xy-kidult/archive/2013/03/25/2970313.html
01背包问题具体例子:假设现有容量10kg的背包,另外有3个物品,分别为a1,a2,a3。物品a1重量为3kg,价值为4;物品a2重量为4kg,价值为5;物品a3重量为5kg,价值为6。将哪些物品放入背包可使得背包中的总价值最大?
这个问题有两种解法,动态规划和贪婪算法。本文仅涉及动态规划。
先不套用动态规划的具体定义,试着想,碰见这种题目,怎么解决?
首先想到的,一般是穷举法,一个一个地试,对于数目小的例子适用,如果容量增大,物品增多,这种方法就无用武之地了。
其次,可以先把价值最大的物体放入,这已经是贪婪算法的雏形了。如果不添加某些特定条件,结果未必可行。
最后,就是动态规划的思路了。先将原始问题一般化,欲求背包能够获得的总价值,即欲求前i个物体放入容量为m(kg)背包的最大价值c[i][m]——使用一个数组来存储最大价值,当m取10,i取3时,即原始问题了。而前i个物体放入容量为m(kg)的背包,又可以转化成前(i-1)个物体放入背包的问题。下面使用数学表达式描述它们两者之间的具体关系。
表达式中各个符号的具体含义。
w[i] : 第i个物体的重量;
p[i] : 第i个物体的价值;
c[i][m] : 前i个物体放入容量为m的背包的最大价值;
c[i-1][m] : 前i-1个物体放入容量为m的背包的最大价值;
c[i-1][m-w[i]] : 前i-1个物体放入容量为m-w[i]的背包的最大价值;
由此可得:
c[i][m]=max{c[i-1][m-w[i]]+pi , c[i-1][m]}(下图将给出更具体的解释)
![](http://images.cnitblog.com/blog/454792/201303/23110036-37762cbba6484ef3a7e3570062b099c9.png)
根据上式,对物体个数及背包重量进行递推,列出一个表格(见下表),表格来自(http://blog.csdn.net/fg2006/article/details/6766384?reload) ,当逐步推出表中每个值的大小,那个最大价值就求出来了。推导过程中,注意一点,最好逐行而非逐列开始推导,先从编号为1的那一行,推出所有c[1][m]的值,再推编号为2的那行c[2][m]的大小。这样便于理解。
![](http://images.cnitblog.com/blog/454792/201303/23110549-c986d3b6e16446a18d47335fd20cc12f.jpg)
思路厘清后,开始编程序,C语言代码如下所示。
如果你很轻松地就突破了01背包,甚至很轻松地就理解了动态规划,那么继续前进,做一下这道题目(http://acm.uestc.edu.cn/problem.php?pid=1012)。很好玩的。
小结:
感谢上面引用的两篇博客,也感谢这两位博主,没有你们的博客,我恐怕对01背包问题还是半懂不懂的。
ps: 2014/5/19 更新一个递归解法,《算法:c语言》似乎也有一个递归解法,不过是错误的:
01背包问题具体例子:假设现有容量10kg的背包,另外有3个物品,分别为a1,a2,a3。物品a1重量为3kg,价值为4;物品a2重量为4kg,价值为5;物品a3重量为5kg,价值为6。将哪些物品放入背包可使得背包中的总价值最大?
这个问题有两种解法,动态规划和贪婪算法。本文仅涉及动态规划。
先不套用动态规划的具体定义,试着想,碰见这种题目,怎么解决?
首先想到的,一般是穷举法,一个一个地试,对于数目小的例子适用,如果容量增大,物品增多,这种方法就无用武之地了。
其次,可以先把价值最大的物体放入,这已经是贪婪算法的雏形了。如果不添加某些特定条件,结果未必可行。
最后,就是动态规划的思路了。先将原始问题一般化,欲求背包能够获得的总价值,即欲求前i个物体放入容量为m(kg)背包的最大价值c[i][m]——使用一个数组来存储最大价值,当m取10,i取3时,即原始问题了。而前i个物体放入容量为m(kg)的背包,又可以转化成前(i-1)个物体放入背包的问题。下面使用数学表达式描述它们两者之间的具体关系。
表达式中各个符号的具体含义。
w[i] : 第i个物体的重量;
p[i] : 第i个物体的价值;
c[i][m] : 前i个物体放入容量为m的背包的最大价值;
c[i-1][m] : 前i-1个物体放入容量为m的背包的最大价值;
c[i-1][m-w[i]] : 前i-1个物体放入容量为m-w[i]的背包的最大价值;
由此可得:
c[i][m]=max{c[i-1][m-w[i]]+pi , c[i-1][m]}(下图将给出更具体的解释)
![](http://images.cnitblog.com/blog/454792/201303/23110036-37762cbba6484ef3a7e3570062b099c9.png)
根据上式,对物体个数及背包重量进行递推,列出一个表格(见下表),表格来自(http://blog.csdn.net/fg2006/article/details/6766384?reload) ,当逐步推出表中每个值的大小,那个最大价值就求出来了。推导过程中,注意一点,最好逐行而非逐列开始推导,先从编号为1的那一行,推出所有c[1][m]的值,再推编号为2的那行c[2][m]的大小。这样便于理解。
![](http://images.cnitblog.com/blog/454792/201303/23110549-c986d3b6e16446a18d47335fd20cc12f.jpg)
思路厘清后,开始编程序,C语言代码如下所示。
#include <stdio.h> int c[10][100]={0}; void knap(int m,int n){ int i,j,w[10],p[10]; for(i=1;i<n+1;i++) scanf("%d,%d",&w[i],&p[i]); for(j=0;j<m+1;j++) for(i=0;i<n+1;i++) { if(j<w[i]) { c[i][j]=c[i-1][j]; continue; }else if(c[i-1][j-w[i]]+p[i]>c[i-1][j]) c[i][j]=c[i-1][j-w[i]]+p[i]; else c[i][j]=c[i-1][j]; } } int main(){ int m,n;int i,j; printf("input the max capacity and the number of the goods:\n"); scanf("%d,%d",&m,&n); printf("Input each one(weight and value):\n"); knap(m,n); printf("\n"); for(i=0;i<=n;i++) for(j=0;j<=m;j++) { printf("%4d",c[i][j]); if(m==j) printf("\n"); } }
代码中,红色字体部分是自己写的,其余的参照了这篇博客http://blog.sina.com.cn/s/blog_6dcd26b301013810.html
如果你很轻松地就突破了01背包,甚至很轻松地就理解了动态规划,那么继续前进,做一下这道题目(http://acm.uestc.edu.cn/problem.php?pid=1012)。很好玩的。
小结:
感谢上面引用的两篇博客,也感谢这两位博主,没有你们的博客,我恐怕对01背包问题还是半懂不懂的。
ps: 2014/5/19 更新一个递归解法,《算法:c语言》似乎也有一个递归解法,不过是错误的:
相关文章推荐
- struts2 小笔记
- Java单链表基本操作(八)--合并两个有序单链表
- Ubuntu 15.10 上使用ppa安装Atom编辑器
- Android:View随手指移动
- [leetcode-329]Longest Increasing Path in a Matrix(java)
- 在安装Ubuntu的POWER服务器上,开启SMT
- Kafka中基于byte[]的murmur2 哈希算法实现
- 类型“System.Windows.Forms.DialogResult”在未被引用的程序集中定义
- 基于MPP架构的TERADATA技术预览
- dellR720服务器设置光盘引导流程安装cenos7
- jetty 通过配置文件嵌入式启动web服务
- 腾讯2016研发工程师编程题
- MATLAB作二维傅里叶变换所需要注意和知道的东西(im2double、fft2、abs、imshow、二维傅里叶变换的物理意义)
- fastjson 使用方法
- 关于编译报错“dereferencing pointer to incomplete type...
- Java集合源码之路-List分析(二)
- CXF实现webservice
- dellR720服务器设置光盘引导流程安装cenos7
- vmware screen
- maven