完全背包问题----思想的理解
来源:互联网 发布:淘宝推广方式 编辑:程序博客网 时间:2024/06/05 00:52
最近学习动态规划,在背包问题处被卡了很久,故在此处把这几天的遇到的问题和自己的理解进行一下总结。
这篇博文主要介绍的是完全背包的思想,包括它的三种状态转移方程。作为引入完全背包问题的前提,附带的介绍了DAG思想和01背包问题。
参考资料有《算法竞赛入门经典》和《背包九讲》。
1.在《算法竞赛入门经典》中,其对完全背包问题是通过DAG的思想来讨论的。先讨论了完全背包问题,再推广到01背包问题。
2.在《背包九讲》中,讨论顺序相反,其对完全背包的讨论是通过01背包的思想推广而来。
一、先来理解《算法竞赛入门经典》的思想。
在看完全背包问题时先来看一个硬币问题。描述如下:
有n中硬币,每种硬币的面值为v1,v1......vn.且每种硬币有无限多。现在给定一个非负整数S,可以选用多少个硬币,使得面值之和恰好为S。输出硬币数目的最小值和最大值。
分析:如何通过建图求解
把要凑足的总面值看成一个个点。起点为S,终点为0.
那么什么样的两个点是联通的呢?很显然点i到点i-vj(i>=vi)是联通的。取硬币j,状态就由i转移到了i-vj,点i与i-vj联通说面这两点之间有一条线。
很显然,从点i出发的线可能是0条,也可能不止一条。
我们现在的问题就变成了从点S到点0,最长的路径和最短的路径。
定义的d[i]为面值之和为i时最大的硬币数目。则有:
d[i] = max(d[i-v[j]]+1) j : 0->n 且 i-v[j]>=0
这个方程叫做状态转移方程。
那么我们是如何来考虑这个状态转移的过程的呢?
即在图中,我们是如何决定,在所有与i联通的点中,选择哪一个点。很显然应该取使d[i-v[j]]最大的那个j.
伪代码如下
(是从硬币的面值总额来考虑这个问题)
d[0] = 0
for j:1->S d[j] =-INF //因为此题要求恰好取到S,要用一个特殊值去表示不能取到的值,否则的话里面是数组随意赋予的值
for i:1->S
do for j:1->n
if(v[j]<=i && d[i]<d[i-v[j]]+1)d[i] = d[i-v[j]]+1;
PA:注意此处两层循环的顺序,第一层循环是对总价值的循环,第二次循环是对硬币种类的循环。《背包九讲》中会把两层循环的顺序改变。
有这个问题来看待完全背包问题,问题描述:
有n种背包,每个包有体积vj重量wj两个属性,每种背包有无穷多个。现有体积为C的大包,问如何装能使大包的重量最大。
分析:
多了一个重量属性,想当于边上有了权值,之前权值为1,现在权值为Wj
定义d[i]为装满体积为i的包是的最大重量
d[i] = max{d[i-v[j]]+w[j]}
二、接着来理解《背包九讲》中的思想
先来看一下01背包问题,描述如下:
有n种背包,每个包有体积vj重量wj两个属性,每种背包只有一个。现有体积为C的大包,问如何装能使大包的重量最大。
这个时候我们重新来定义一下状态转移方程。
d[i][j] 表示取前i种包装到体积为j的包中时所能达到的最大重量,则有:
d[i][j] =max{ d[i-1][j],d[i-1][j-c[i]]+w[i]}
其实这个转移方程也很好理解,无非就是有i-1种包转移到i种包时,第i个包到底取不取的问题。
伪代码如下:
d[0][i] =0;
d[i][0] =0;
for i:1->n
do for j:c[i]->C
d[i][j] = max{d[i-1][j],d[i-1][j-c[i]]+w[i]}
对以上代码可以把的d优化为一维的数组
伪代码如下:
d[0....C] =0;
for i:1->n
do for j:C->c[i] (注意j历遍的顺序变了,上面的伪代码是由c[i]->C,而这里的伪代码是由C->c[i])
d[j] = d[j] >d[j-c[i]]]+w[i]?d[j]:d[j-c[i]]]+w[i]
如何由一维数组到二维数组的呢?
在二维数组的情况中,对于i的每一个值,都能算出一个d[i,0.....C]与其对应。那么,能否保证第i次循环后,d[v]是d[i][v]的值呢?
d[i][v]是由d[i-1][v],d[i-1][v-c[i]]+w[i]递推得到的.所以要保证在第i次循环的时候,d[v]中的值是d[i-1,v]的值。
j变为逆序就可以满足上述条件。(如果难以理解,最好上机调试单步查看结果)
如果是由c[i]到C的顺序执行,则d[v-c[i]]一定会在d[v]前面更新,这是更新d[v]时d[v-c[i]]中的值已经是d[i,v-c[i]],而不是d[i-1,v-c[i]]了。
由01背包问题到完全背包问题:
首先看二维的方式,二维的时候很好定义状态转移方程
d[i][j]仍是前i种背包装入到j体积中时的最大重量,那么
d[i][j] = max{d[i][j-k*c[i]]+k*c[i]}, k:0->j/c[i]
同样二维的可以简化为一
伪代码如下:
d[0....C] =0;
for i:1->n
do for j:c[i]->C (注意此时j的历遍顺序恰好和01背包相反,而此时两个for循环的顺序与《算法竞赛入门》中for循环的顺序恰好相反,
《背包九讲》里也提到两个for循环的顺序可以互换,就在此处体现。要注意不是任何情况是两个for循环都可以替换,
如UVA674硬币找零问题, 后面会有专门的文章总结。)
d[j] = d[j] >d[j-c[i]]]+w[i]?d[j]:d[j-c[i]]]+w[i]
- 完全背包问题----思想的理解
- 背包问题的解题思想
- 01背包、完全背包、多重背包问题的C++实现
- 完全背包与01背包的粗浅理解
- 多重背包问题的二进制分解思想
- 背包问题思想的一个应用
- 多重背包问题的二进制分解思想
- 01背包问题的理解
- 对背包问题的理解
- 背包问题的自己理解
- 背包问题基本思想
- 完全背包问题的改进实现
- P1616 疯狂的采药,完全背包问题
- 背包问题-完全背包-背包问题
- 01背包 完全背包问题
- 背包问题2:完全背包
- 01背包+完全背包问题
- 背包问题-完全背包-贪吃的大嘴
- Android开发——AsyncTask的使用以及源码解析
- 模拟栈问题(思路)
- IOS masonry动画
- Activiti 查看流程历史记录
- 【HDU 2181】哈密顿绕行世界问题(DFS)
- 完全背包问题----思想的理解
- Spring容器中Bean的作用域之singleton和prototype
- 数据结构 并查集 POJ 2236
- 安卓短信验证码发送
- c++进程间通讯 为什么不能用PostMessage传递指针数据
- Scala(5)
- mysql 个人小总结
- UVA 1600 Patrol Robot
- 山东北方盐化MT4交易系统 v4.00.940官方版