[算法][动态规划]背包问题变体-均分礼物

均分礼物

今天遇到的一个面试手撕题:

给定一个礼物价值清单,需要将其进行划分为两个子集,以使得两个子集的价值和的差值最小。

思路

[算法][动态规划][背包问题①]0-1背包问题的优化及约束变形[python实现

  1. 将其视为一个0-1背包问题
  2. 取总价值的一半作为背包容量
  3. 尽量填满这个背包(此问题中 w i w_i wi= v i v_i vi)

d p [ j ] = m a x { d p [ j − w i ] + v i , d p [ j ] } dp[j]=max\{dp[j−w_i]+v_i ,dp[j]\} dp[j]=max{dp[jwi]+vi,dp[j]}

  1. 总价值减去半背包的最优价值即可

代码

class Solution:
    def maxPresent(self , presentVec):
        # 把总体积的一半作为背包容量
        all_volume = sum(presentVec)
        half_volume = all_volume//2
        dp = [0] * (half_volume+1)
        for w in presentVec:# 第i个物品
            for j in range(half_volume, w-1, -1):
                dp[j] = max(dp[j], w + dp[j-w])
        return all_volume - 2*dp[-1]

如果要求应对输出两个子集中具体物品内容:

class Solution:
    def maxPresent(self , presentVec ):
        # 把总体积的一半作为背包容量
        all_volume = sum(presentVec)
        half_volume = all_volume//2
        dp = [0] * (half_volume+1)
        trace = [[] for _ in range(half_volume + 1)] # 添加一个轨迹
        for w in presentVec:# 第i个物品
            for j in range(len(dp)-1, w-1, -1):
                new_price = w + dp[j-w]
                if new_price > dp[j]:
                    dp[j] = new_price
                    trace[j] = trace[j-w] + [w] # 重写轨迹
        return all_volume - 2*dp[-1], trace[-1] # 最小差值和其中一个子集

测试:

s = Solution()
print(s.maxPresent([41,467,334,1,169,224,478,358])) 
# 最小差值为0,其中一个子集为:[334, 224, 478]

你可能感兴趣的:(算法,面试,面试手撕)