【9】背包问题的实际应用
1. 简单扩展,加上“最多可以放多少件物品或者最多可以装满多少背包的空间”类似的限制条件,同样可以先求出动态规划矩阵值,然后再在里面搜
2. 打印出最优方案,则需要对每一步动态规划过程,记录下它的选择g[i][v]
3. 输出字典序最小,N到1输入,需要注意的是,如果跳过i与选择i结果一样时,应该选择i
4. 装满背包或将背包装至某一指定容量的方案总数,f[i][v]=sum{f[i-1][v],f[i][v-c[i]]},初始条件f[0][0]=1
5. 最优方案的总数,声明一个记录数目的矩阵,在普通动规过程中,加一个判断,如果跳过,选择,或者两者都有,则进行相应的加法
6. 第K优解,转变一下:状态f[i][v]就应该是一个大小为K的数组f[i][v][1..K],有序队列f[i-1][v]即f[i-1][v][1..K],f[i-1][v-c[i]]+w[i]则理解为在f[i-1][v-c[i]][1..K]的每个数上加上w[i]后得到的有序队列,合并这两个有序队列并将结果的前K项储存到f[i][v][1..K]中的复杂度是O(K)。最后的答案是f[N][V][K]。总的复杂度是O(VNK)。
注意:背包问题的动态规划解法是个伪多项式解法,O(VN)与V有关,V巨大时,是没法求解的。
子集和问题是一个NP-Complete问题,与前述的(加权的)01背包问题并不相同。给定一个整数的集合S和一个整数X,问是否存在S的一个子集满足其中所有元素的和为X。这个问题有一个时间复杂度为O(2^(N/2))的较高效的搜索算法,其中N是集合S的大小。第一步思想是二分。将集合S划分成两个子集S1和S2,它们的大小都是N/2。对于S1和S2,分别枚举出它们所有的2^(N/2)个子集和,保存到某种支持查找的数据结构中,例如hash set。然后就要将两部分结果合并,寻找是否有和为X的S的子集。事实上,对于S1的某个和为X1的子集,只需寻找S2是否有和为X-X1的子集。假设采用的hash set是理想的,每次查找和插入都仅花费O(1)的时间。两步的时间复杂度显然都是O(2^(N/2))。实践中,往往可以先将第一步得到的两组子集和分别排序,然后再用两个指针扫描的方法查找是否有满足要求的子集和。这样的实现,在可接受的时间内可以解决的最大规模约为N=42。
搜索还是DP?
首先,可以从数据范围中得到命题人意图的线索。如果一个背包问题可以用DP解,V一定不能很大,否则O(VN)的算法无法承受,而一般的搜索解法都是仅与N有关,与V无关的。所以,V很大时(例如上百万),命题人的意图就应该是考察搜索。另一方面,N较大时(例如上百),命题人的意图就很有可能是考察动态规划了。另外,当想不出合适的动态规划算法时,就只能用搜索了。例如看到一个从未见过的背包中物品的限制条件,无法想出DP的方程,只好写搜索以谋求一定的分数了。