[LeetCode-Python版]动态规划——0-1背包和完全背包问题总结

0-1背包

有n个物品,第i个物品的体积为 w i w_i wi,价值为 v i v_i vi,每个物品至多选一个,求体积和不超过 capacity 时的最大价值和
状态转移:
d f s ( i , c ) = m a x ( d f s ( i − 1 , c ) , d f s ( i − 1 , c − w [ i ] ) + v [ i ] dfs(i,c)=max (dfs(i- 1,c),dfs(i- 1,c- w[i]) + v[i] dfs(i,c)=max(dfs(i1,c),dfs(i1,cw[i])+v[i]

常见变形

  1. 至多装 capacity,求方案数/最大价值和
  2. 恰好装 capacity,求方案数/最大/最小价值和
  3. 至少装 capacity,求方案数/最小价值和

处理求“方案数”、“最大价值和”和“最小价值和”这三种情况时,代码的主要区别在于状态转移方程
(方案数是+,最大价值和求max,最小价值和求min)

  • 方案数: d f s ( i , c ) = d f s ( i − 1 , c ) + d f s ( i − 1 , c − w [ i ] ) dfs(i,c)= dfs(i- 1,c)+dfs(i- 1,c- w[i]) dfs(i,c)=dfs(i1,c)+dfs(i1,cw[i])

  • 最大价值和: d f s ( i , c ) = m a x ( d f s ( i − 1 , c ) , d f s ( i − 1 , c − w [ i ] ) + v [ i ] dfs(i,c)=max (dfs(i- 1,c),dfs(i- 1,c- w[i]) + v[i] dfs(i,c)=max(dfs(i1,c),dfs(i1,cw[i])+v[i]

  • 最小价值和: d f s ( i , c ) = m i n ( d f s ( i − 1 , c ) , d f s ( i − 1 , c − w [ i ] ) + v [ i ] dfs(i,c)=min (dfs(i- 1,c),dfs(i- 1,c- w[i]) + v[i] dfs(i,c)=min(dfs(i1,c),dfs(i1,cw[i])+v[i]

例子

  1. 目标和
    求恰好装capacity的方案数

    class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        # dfs(i,t) = dfs(i-1,t+nums[i])+dfs(i-1,t-nums[i])
        # 加正号的数和为p
        # 加负号的就是所有数的和s减去加正号的数p s-p是所有负数不带负号的累加和
        # 这里的p和(s-p)代表的是正1和负1的个数,是个数!!!
        # target = p - (s-p)
        # 2p = s+target
        # p = (s+target)/2 s+t 必然是一个正的偶数
        s_add_t = sum(nums)+target
        if s_add_t <0 or s_add_t%2:
            return 0
        s_add_t//=2
        n = len(nums)
        @cache
        def dfs(i,t):
            if i<0:
                return 1 if t==0 else 0
            if t<nums[i]:
                return  dfs(i-1,t)
            return dfs(i-1,t)+dfs(i-1,t-nums[i])
        return dfs(n-1,s_add_t)
    
  2. 和为目标值的最长子序列的长度
    恰好装capacity的最大价值和
    但是这题用记忆化搜索做会超时超内存

    class Solution:
        def lengthOfLongestSubsequence(self, nums: List[int], target: int) -> int:
            n = len(nums)
            
            @cache
            def dfs(i,c):
                if i<0:
                    return 0 if c==0 else -inf
                if c<nums[i]:
                    return dfs(i-1,c)
                return max(dfs(i-1,c),dfs(i-1,c-nums[i])+1)
    
            res = dfs(n-1,target)
            dfs.cache_clear() # 不加会超出内存限制
            return res if res>-inf else -1
    
  3. 分割等和子集
    恰好装capacity的方案数(变体,只要有一个就返回True)

    class Solution:
        def canPartition(self, nums: List[int]) -> bool:
            n = len(nums)
            s = sum(nums)
            if s%2: return False
            target = s/2
            @cache
            def dfs(i,c):
                if i<0:
                    return c==0
                if c<nums[i]:
                    return dfs(i-1,c)
                return dfs(i-1,c) or dfs(i-1,c-nums[i])
            return dfs(n-1,target)
            
    

完全背包

有n个物品,第i个物品的体积为 w i w_i wi,价值为 v i v_i vi每种物品无限次重复选,求体积和不超过 capacity 时的最大价值和
d f s ( i , c ) = m a x ( d f s ( i − 1 , c ) , d f s ( i , c − w [ i ] ) + v [ i ] dfs(i,c)=max (dfs(i- 1,c),dfs(i,c- w[i]) + v[i] dfs(i,c)=max(dfs(i1,c),dfs(i,cw[i])+v[i]

常见变形

  • 至多装 capacity,求方案数/最大价值和
  • 恰好装 capacity,求方案数/最大/最小价值和
  • 至少装 capacity,求方案数/最小价值和

处理求“方案数”、“最大价值和”和“最小价值和”这三种情况时,代码的主要区别在于状态转移方程
(和0-1背包的唯一区别是选的情况的i-1改成i,因为可以重复选)

  • 方案数: d f s ( i , c ) = d f s ( i − 1 , c ) + d f s ( i , c − w [ i ] ) dfs(i,c)= dfs(i- 1,c)+dfs(i,c- w[i]) dfs(i,c)=dfs(i1,c)+dfs(i,cw[i])

  • 最大价值和: d f s ( i , c ) = m a x ( d f s ( i − 1 , c ) , d f s ( i , c − w [ i ] ) + v [ i ] dfs(i,c)=max (dfs(i- 1,c),dfs(i,c- w[i]) + v[i] dfs(i,c)=max(dfs(i1,c),dfs(i,cw[i])+v[i]

  • 最小价值和: d f s ( i , c ) = m i n ( d f s ( i − 1 , c ) , d f s ( i , c − w [ i ] ) + v [ i ] dfs(i,c)=min (dfs(i- 1,c),dfs(i,c- w[i]) + v[i] dfs(i,c)=min(dfs(i1,c),dfs(i,cw[i])+v[i]

例子

  1. 零钱兑换
    求恰好装capacity的最小价值和
    class Solution:
        def coinChange(self, coins: List[int], amount: int) -> int:
            # 硬币的面值 表示容量 然后硬币数量 每次选的话 就是1 相当于结果数
            n = len(coins)
            @cache
            def dfs(i,c):
                if i<0:
                    return 0 if c==0 else inf
                if c<coins[i]:
                    return dfs(i-1,c)
                return min(dfs(i-1,c),dfs(i,c-coins[i])+1)
            res = dfs(n-1,amount)
            return res if res<inf else -1
            
    

你可能感兴趣的:(LeetCode-Python,leetcode,python,动态规划)