01背包巧用与强化
ACM-HDOJ 2602(01背包水题)
Problem Description
Output
Sample Input
Sample Output
题目要求
01背包水题。只是把物品变成了骨头。
参考AC代码
|
|
思路
参考DP专题一的01背包介绍,此题完全套状态方程。
注意事项
内循环逆序。
ACM-HDOJ 2639(01背包第k优解)
Problem Description
Input
Output
Sample Input
Sample Output
题目要求
在2602题的基础上求第k优解。
参考AC代码
|
|
思路
对于求次优解、第K优解类的问题,如果相应的最优解问题能写出状态转移方程、用动态规划解决,那么求次优解往往可以相同的复杂度解决,第K优解则比求最优解的复
杂度上多一个系数K。其基本思想是将每个状态都表示成有序队列,将状态转移方程中的max/min转化成有序队列的合并。这里仍然以01背包为例讲解一下。首先看01背包求
最优解的状态转移方程:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}。如果要求第K优解,那么状态f[i][v]就应该是一个大小为K的数组f[i][v][1..K]。其中f[i][v][k]表
示前i个物品、背包大小为 v时,第k优解的值。“f[i][v]是一个大小为K的数组”这一句,熟悉C语言的同学可能比较好理解,或者也可以简单地理解为在原来的方程中加了一维。
显然f[i][v][1..K]这K个数是由大到小排列的,所以我们把它认为是一个有序队列。然后原方程就可以解释为:f[i][v]这个有序队列是由f[i-1][v]和f[i-1][v-c[i]]+w[i]这两
个有序队列合并得到的。有序队列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)。
为什么这个方法正确呢?实际上,一个正确的状态转移方程的求解过程遍历了所有可用的策略,也就覆盖了问题的所有方案。只不过由于是求最优解,所以其 它在任何一
个策略上达不到最优的方案都被忽略了。如果把每个状态表示成一个大小为K的数组,并在这个数组中有序的保存该状态可取到的前K个最优值。那么, 对于任两个状态的max
运算等价于两个由大到小的有序队列的合并。另外还要注意题目对于“第K优解”的定义,将策略不同但权值相同的两个方案是看作同一个解还是不同的解。如果是前者,则维护
有序队列时要保证队列里的数没有重复的。
用个形象的比喻吧:如果我想知道学年最高分,那么,我只要知道每个班级的最高分,然后统计一遍就可以了。如果我想知道学年前十呢?我必须要知道每个班的前十名。
大家在心里模拟一下,对,这就是本题核心的算法。两种决策,就可以看作这个学年只有两个班。
根据以上思路,将原来的dp[i]扩展成dp[i][j]表示背包容量用了i的第j优解。
注意事项
分别使用a,b两个数组存储两种状态,再合并求最大值。
ACM-HDOJ 2955(01背包巧用)
Problem Description
Input
Output
Sample Input
Sample Output
题目要求
小偷抢银行,给出银行的个数,每个银行中的存款和每个银行中被抓的概率,求出在小于给定概率的情况下抢得的最大值。
参考AC代码
|
|
思路
1、抢n个银行,不被抓,是每次都不被抓,用乘法原理处理每次不被抓的概率。
2、转化为01背包:将所有银行的总钱数当做背包的容积v,将不被抓的概率当做cost[i],
在一般的01背包中都是加上cost[i] ,这里由于是乘法原理,所以应该是转化为乘法。
这样,传统的01背包累加问题变成了垒乘。
3、状态转移方程:dp[j]=max(dp[j],dp[j-bank[i].money]*bank[i].caught)
dp[j]表示抢j钱时的最大不被抓概率。
注意事项
此题乍看与背包问题无关,其实是变相的01背包问题。由于dp求得的是最大的不被抓概率,所以程序中的p和bank[i].caught必须转化为不被抓的概率,即用1减去本身。
最后在判断的时候需注意i从sum开始,即从最大背包容量开始判断,每个容量对应的最大不被抓概率是否满足小于给定的概率,若满足则立刻输出并结束循环,保证输出
的是最优化值。dp的初始化也要注意给dp[0]赋为1,因为当抢钱数为0是不会被抓。
ACM-HDOJ 3466(01背包排序)
Problem Description
Input
Output
Sample Input
Sample Output
题目要求
用钱买商品,每件物品只有在大于特定资金才能进行交易,在总资金一定的情况下求获得的最大价值。
参考AC代码
|
|
思路
此道01背包问题需要sort排序,理由如下:
有两个物品(p1,q1,v1),(p2,q2,v2),然后物品1先放的话,物品2就可以借助物品1产生的各种状态来进行下一步转移,而如果物品2的q2值过高,在这个[q2,m]的区间内都
不存在物品1造成的新状态的话,那么物品1的状态就没有得到利用。而如果交换顺序,先放了物品2,那么显然物品1就可以利用物品2产生的新状态。
所以物品1能从物品2转移的状态区间其实是[min(q1+p2,m),m],物品2能从物品1转移的状态区间是[min(q2+p1,m),m]。所以尽可能地复用这个区间,让区间小的先来,
区间大的后来,这样排序之后所有物品都能从前面的物品得到新状态进行转移。
先买A,至少需要p1+q2,先买B,至少需要p2+q1;若A q2-p2;即差值大的先买,所以先买的应排到后面。
而普通的01背包之所以不需要排序,是因为p1==q1,p2==q2,排序跟不排是一回事。这一类的dp题要注意后效性是否存在,如果存在通过改变顺序之类的办法来取消后效性。
在ACM-HDOJ 2546题中,qi恒定为5,所以也是需要排序的。
注意事项
因为01背包的内循环是逆序,所以在先买差值大的情况下,a数组应从小到大进行排列,确保在循环中先从末尾的最大买起。
ACM-HDOJ 1864(01背包变形)
Problem Description
Input
Output
Sample Input
Sample Output
参考AC代码
|
|
思路
依次搜索每张发票的信息,判断是否满足题给的三个条件:只有ABC三类,ABC单项小于600,ABC和小于1000.若均满足后存入money数组,之后对该数组进行01背包的
操作后即可得出结果。
注意事项
由于该题要求有关数据均要保留两位小数,所以为了简化操作,把所有的小数均扩大100倍后进行整型操作,最后只需要把结果在除以100.0即可。注意l的定义要在for循环之
外,因为money数组存放的是所有满足条件发票的总资金。只要有一个不是ABC类型的flag=0,不满足if判断直接进行下一张发票的判断。dp数组应该开到30×1000×100的
大小,防止数组越界。(多乘的100是因为扩大了100倍)