这周不打算按照以往的方式更新博客,而是采用整体的方式。一是因为学的太少,没东西写;二是这篇博客会经常更新的。如题,DP——尽力学习之中。
------------------------------------------------------------------------------------------------------------------------------------------------------------------
先说几个与训练不太相关的东西
一、DP分类:
基础DP、线形DP、概率DP、区间DP、树形DP、数位DP、状态压缩DP......
------------------------------------------------------------------------------------------------------------------------------------------------------------------
二、DP问题满足的性质:
①最优子结构性质:如果问题的最优解所包含的子问题的解也是最优的,则称该问题具有最优子结构性质(也称满足最优化原理)。
②子问题重叠性质:在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划利用这种子问题的重叠性质,对每一个子问题只计算一次(记忆化搜索),然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而效率较高。
③无后效性:将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
------------------------------------------------------------------------------------------------------------------------------------------------------------------
心得总结:
DP之一:基础DP(背包) 借鉴资料——背包九讲
Case1:0 1背包:有N件物品,每种物体只有一种(第i种物品的价值是value[i],所占空间是volume[i]),决策只有拿与不拿。给定背包总空间V,求在不超过V的情况下的 MAX value。
若用f[i][V]表示前i件物品恰放入一个容量为V的背包可以获得的最大价值,对于物品i,两种策略:
1.如果拿:则value = ( f[i-1][ V-volume[i] ] ) + value[i]; //前i-1件物品value+第i件物品value
2.如果不拿:则value = f[i-1][V].
于是很容易得到状态转移方程:f[i][V] = max( f[i-1][V] , f[i-1][V-volume[i] + value[i] )
采用一维数组的话:用f[0...V]表示,f[V]表示把前i件物品放入容量为V的背包里得到的价值。
表示方法:dp[j] = max ( dp[ j ] , dp[ j - volume[i] ] + value[i] )
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Case2:完全背包:有N件物品,每种物体有无限种(第i种物品的价值是value[i],所占空间是volume[i]),决策是拿多少件。给定背包总空间V,求在不超过V的情况下的 MAX value。
0 1背包和完全背包非常相似,只是一个顺序(对于物品i,完全背包可以拿多次,所以是顺序,0 1背包只能拿一次,所以是逆序)不同,为方便起见,把两者的核心代码写在一起。
顺便提一下,在顺序上,与你采用的数组有关。用二维数组的话,0 1背包也是可以顺序的。但是我们为了节约空间,一般采用一维数组,所以要注意顺序。
for(int i=0; i<n; i++)//n件物品 { for(int j=m; j>=volume[i]; j--)//逆序->0 1背包 //for(int j=volume[i]; j<=m; j++)//顺序->完全背包 dp[j] = max( dp[j], dp[ j - volume[i] ] + value[i] );//比较第i种与第j种所得价值的大小 } cout << dp[m] << endl;
训练中遇到的:
第四周训练的M题(0 1 背包)http://acm.hust.edu.cn/vjudge/contest/125308#problem/M
第五周训练的G题(完全背包)http://acm.hust.edu.cn/vjudge/contest/126708#problem/G
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Case3:多重背包:有N件物品,第i种物体有n[i]种(第i种物品的价值是value[i],所占空间是volume[i])。给定背包总空间V,求在不超过V的情况下的 MAX value。
对于多重背包,可在完全背包的基础上进行修改,与之不同的是,对于第i件物品,有n[i]+1种策略(取0~n[i]件)。
于是可以得出状态转移方程(二维数组表示):f[i][V] = max( f[i-1][V] , f[i-1][V - k*volume[i]] + k*value[i])(0<=k<=n[i] )
一维数组表示:dp[j] = max( dp[j] , dp[i-1][V - k*volume[i]] + k*value[i] )
如果数据量比较大,多重背包复杂度比较高,建议优化。下面仅提供几种参考
1.若两件物品i、j满足volume[i]<=volume[j]且value[i]>=value[j],则将物品j去掉,这个优化显然正确。因为任何情况下都可将价值小费用高得j换成物美价廉的i,得到至少不会更差的方案。然而这个并不能改善最坏情况的复杂度,因为有可能特别设计的数据可以一件物品也去不掉。这个优化可以简单的O(N^2)地实现,一般都可以承受。
2.针对背包而言,可以首先将费用大于V的物品去掉,然后使用类似计数排序的做法,计算出费用相同的物品中价值最高的是哪个,可以O(V+N)地完成这个优化。
3.还可以考虑把完全背包转化为0 1背包来解,最简单的想法是:将一种物品拆成多件物品。
4.更高效的转化方法是(二进制优化):把第i种物品拆成费用为volume*2^k、价值为value*2^k的若干件物品,其中k满足0<=k<=log2(V/volume)+1。这是二进制的思想,因为不管最优策略选几件第i种物品,总可以表示成若干个2^k件物品的和。这样把每种物品拆成O(log2(V/volume))件物品,这是一个很不错的优化。
训练中遇到的:第五周的H题:http://acm.hust.edu.cn/vjudge/contest/126708#problem/H
背包暂时写到这里,混合背包看着有点晕,以后更新。
------------------------------------------------------------------------------------------------------------------------------------------------------------------
DP之二:线性DP(不定时更新中......)
Case1:最长递增子序列问题(LIS)
Case2:最长公共子序列问题(LCS)
Case3:子集和问题(subset sum)