动态规划-01背包问题

Posted by Annie's Blog on June 12, 2016

可以用动态规划求解的问题的特点

最优化问题

  • 最优子结构性质:当问题的最优解包含了其子问题的最优解时,成该问题具有最优子结构性质

  • 重叠子问题:每次产生的子问题并不总是新问题,有些子问题被反复计算多次

满足以上两个特点的问题可考虑用动态规划求解

例子1 背包问题

问题

给定n种物品和一背包,物品i的重量是Wi,其价值为Vi,背包的容量为C。问应如何选择装入背包的物品(物品不能分割),使得装入背包中物品的总价值最大?

问题抽象化

用Xi表示是否把物品i放入背包中 Xi = 1 表示把物品i放进背包中 Xi = 0 表示不把物品i放进背包中

我们要求的是

image

问题分析

m[i, j]表示 在物品i, i+1, i+2, …, n中选择放入到背包中的物品时价值最大的组合

j表示背包此时的容量

m[0][C]就是我们要求的值

可得推导公式如下:

m[i][C'] = max{ m[i+1][C'], m[i+1][C'-w[i]]+v[i] }

即最优化结果为:

‘不放入第i种物品:剩下不考虑第i种物品时的最优化结果’ 和 ‘放入第i种物品:第i种物品的价值+剩下不考虑第i种物品时的最优化结果’ 中的较大值

假设:

n=3,c=6,w={4,3,2} v={5,2,1}

则 我们要求的是m[0][6]

边界条件:

当i>=3时,m=0
当j<=0时,m=0

推导过程如下

m[0][6] = max{ m[1][6], m[1][6-w[0]]+v[0] }
	    = max{ m[1][6], m[1][2]+v[0] }
	    = 6
	    
	m[1][6] = max{ m[2][6], m[2][6-w[1]]+v[1] }
			= max{ m[2][6], m[2][3]+v[1] }	
			=3		
		
		m[2][6] = max{ m[3][6], m[3][6-w[2]+v[2]] }
	         	= max{ m[3][6], m[3][4]+v[2] }
        		= 1
        
		m[2][3] = max{ m[3][3], m[3][3-w[2]]+v[2] }
				= max{ m[3][3], m[3][1]+v[2] }
				=1
        
	m[1][2] = max{ m[2][2], m[2][2-w[1]]+v[1] }
        	= max{ m[2][2], m[2][-1]+v[1] }
        	= 1
        
		m[2][2] = max{ m[3][2], m[3][2-w[2]]+v[2] }
        		= max{ m[3][2], m[3][0]+v[2] }
        		= 1

例子2 奖券兑换问题

背包问题的一个变形

问题

小Ho现在手上有C张奖券,而奖品区有N件奖品,分别标号为1到N,其中第i件奖品需要w(i)张奖券进行兑换,同时也只能兑换一次,为了使得辛苦得到的奖券不白白浪费,小Ho给每件奖品都评了分,其中第i件奖品的评分值为v(i),表示他对这件奖品的喜好值。现在他想知道,凭借他手上的这些奖券,可以换到哪些奖品,使得这些奖品的喜好值之和能够最大。

问题抽象化

用Xi表示是否兑换第i种奖品

Xi = 1表示兑换

Xi = 0表示不兑换

我们要求的是

image

问题分析

opt[i, C']表示用C’张奖券换i, i+1, …, n-1种奖品时评分的最大值

C’表示所需奖券C’张

opt[0, C]就是我们要求的值

可得推导公式如下:

opt[i][C'] = max{ opt[i+1][C'], opt[i+1][C'-w[i]]+v[i] }

即最优化结果为:

‘不兑换第i种物品:剩下不考虑第i种物品时的最优化结果’ 和 ‘兑换第i种物品:第i种物品的价值+剩下不考虑第i种物品时的最优化结果’ 中的较大值

代码实现


w = [144, 487, 210, 567, 1056]
v = [990, 436, 673, 58, 897]

opt = {}

def cal(n, c):
    if n >= ngifts or c <= 0:
        return 0
    if opt.get((c, n)):
        return opt[(c, n)]
    else:
        ret = -1
        for k in range(2):
            temp = cal(c - k*w[n], n+1)
            if c - k*w[n] >= 0:
                temp += v[n]*k
                if temp > ret:
                   ret = temp
        opt[(n, c)] = ret
        return ret

print cal(0, 1000)

例子3 饮料供货问题

背包问题的一个复杂版本

编程之美P43