01背包问题小结

这两天做了下01背包的题,简单的总结下

首先,对于最基本的01背包问题,转移方程为dp[i][j]=max{dp[i-1][j],dp[i-1][j-cost[i]]+w[i]}

表示考虑第i件物品,容量为j时,有两种策略,第一种是不选该物品,第二种为选择该物品,在这两种策略中选择一个最优策略

复杂度O(VN)

 

伪代码

for i=1 to n

   for v=0 to V

      if(v>=cost[i])

          dp[i][v]=max(dp[i-1][v],dp[i-1][v-cost[i]]+w[i])

 

如果用滚动数组压缩空间:

 

for i=1 to n

   for v=V to 0

      if(v>=cost[i])

          dp[v]=max(dp[v],dp[v-cost[i]]+w[i])

 

注意到将容量从大到小开始循环后,就可以用一维的数组表示了,当然,这得当所有物品容量都为正时才是正确的

 

对于01背包问题,每种物品只考虑一次,且每种物品只能被加入一次,而这也恰是为什么第二层循环中v要从大到小的原因,因为当物品容量为正时,容量较大的情况由容量较小的情况决定,如果从小的开始推,一中物品就有可能被加入背包多次

一维的代码中dp[v]在被更新前就相当于dp[i-1][v],我们将容量从大到小开始推的话,设v1>v2,我们更新v1时,v2还未被更新,表示二维中的dp[i-1][v],一旦被更新后,就表示dp[i][v]了,如果我们从小往大推的话,会有可能出现这样的情况:dp[v2]被物品i更新一次,更新dp[v1]时又由dp[v2]与背包i更新一次,这样,dp[v1]保存的信息中,物品i就被装了两次,这与01背包的定义是不同的

 

当然,如果此时物品容量为负时,我们必须将v从小到大推,道理是一样的

 

POJ 3132 Sum of Different Primes

求一个数n由k个不同素数组成的方案数,其中,5=2+3和5=3+2是一样的,即不考虑顺序

这道题可以转化成01背包求解,n小于等于1120,k<=14,我们可以先打表打出1120内的素数,然后把这187个素数看成187件物品,然后他们的和看成容量,

即1<=n<=187,1<=V<=1120

但是,这道题又多了一个维数的限制,即装入物品个数的限制,没关系,递推就行

dp[i][j][k]表示前i个素数中的j个素数组成和为k的方案数,那么dp[i][j][k]=sum{dp[i-1][1..j-1][k-prime[i]]

压缩空间后可以去掉一维i,但是k要从大到小枚举,因为不能用重复的素数!

 

代码:

 

POJ 2184 Cow Exhibition

也可以转化为01背包来做,只是这题需要一些变化,把s看成容量,f看成价值
则dp[i][j]=选前i头牛且s的和为j时,f的最大值,最后枚举满足条件的s和与它对应的f,由于i可能为负,需要增加del值平移
当然,也可以压缩为一维的,但是需要分情况考虑fi大于0和小于0的情况,保证第i头牛只被加1次
还有,这题可以设置i的左右边界优化下,我从200多跑到63ms了
代码:
 
POJ 1837 Balance
与2184差不多,不过这题求的是方案数,又与3132相似
首先定义状态方程为dp[i][j]为选前个重物且力矩为j时的方案数,则dp[i][j]=sum{ dp[i-1][j-pos[k]]},1<=k<=m,m为挂钩的个数,pos[k]为第k个挂钩的位置
那么这题能否也压缩一下呢?答案是不行的,因为这题转移时需要在同一个维度i枚举不同的挂钩,不管你考虑pos[k]为正时倒着来也好,pos[k]为负时正着来也好,都有可能将第i个挂钩多次装入背包,与上面两个题可以放一起体会体会
最后结果为dp[n][0+del]

代码:
 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值