1. 程式人生 > >【leetcode】揹包問題彙總

【leetcode】揹包問題彙總

詳細解析參考“揹包九講”:https://github.com/tianyicui/pack

目錄

1. 揹包問題 I —— 0-1揹包無價值

2. 揹包問題II —— 0-1揹包有價值

3. 揹包問題III —— 完全揹包問題

小結1

4. 揹包問題IV / V —— 求方案數

5. 揹包問題VII —— 多重揹包問題

6. 多重揹包可行性解

7. 換零錢問題

 


1. 揹包問題 I —— 0-1揹包無價值

0-1揹包問題,無價值:https://www.lintcode.com/problem/backpack/description

問題描述:Given n items with size Ai, an integer m denotes the size of a backpack. How full you can fill this backpack?

解析:

遞推公式:dp[i][j] = max(dp[i-1][j-A_i] + A_i, dp[i-1][j])

dp[i][j]:前i個物品放入容量為j的揹包的最大重量;

dp[i-1][j-A_i] + A_i:放入第i個時的重量;

dp[i-1][j]:不放第i個時的重量;

第一層迴圈 i:0~n

第二層迴圈 j:m ~ Ai,倒序是因為每個物品只能用一次。

可簡化成一位陣列。

class Solution:
    """
    @param m: An integer m denotes the size of a backpack
    @param A: Given n items with size A[i]
    @return: The maximum size
    """
    def backPack(self, m, A):
        n = len(A)
        if n <= 0 or m <= 0:
            return 0
        dp = [0 for _ in range(m+1)]
        for i in range(n):
            for j in range(m,A[i]-1,-1):
                dp[j] = max(dp[j-A[i]] + A[i], dp[j])
        return dp[-1]

2. 揹包問題II —— 0-1揹包有價值

0-1揹包問題有價值:https://www.lintcode.com/problem/backpack-ii/

問題描述:

Given n items with size Ai and value Vi, and a backpack with size m. What's the maximum value can you put into the backpack?

Example:

Given 4 items with size [2, 3, 5, 7] and value [1, 5, 2, 4]

, and a backpack with size 10. The maximum value is 9.

思路:同上

class Solution:
    """
    @param m: An integer m denotes the size of a backpack
    @param A: Given n items with size A[i]
    @param V: Given n items with value V[i]
    @return: The maximum value
    """
    def backPackII(self, m, A, V):
        # write your code here
        n = len(A)
        if m <= 0 or n <= 0:
            return 0
        dp = [0 for _ in range(m + 1)]
        for i in range(n):
            for j in range(m,A[i]-1,-1):
                dp[j] = max(dp[j-A[i]]+V[i], dp[j])
        return dp[m]

3. 揹包問題III —— 完全揹包問題

完全揹包問題,有價值無限個數:https://www.lintcode.com/problem/backpack-iii/description

問題描述:

Given n kind of items with size Ai and value Vi( each item has an infinite number available) and a backpack with size m. What's the maximum value can you put into the backpack?

Example:

Given 4 items with size [2, 3, 5, 7] and value [1, 5, 2, 4], and a backpack with size 10. The maximum value is 15.

思路:

遞推公式:dp[i][j] = max(dp[i][j-A_i] + V_i, dp[i-1][j])​​​​​​

dp[i][j]:同理;

dp[i][j-A_i] + V_i:放入第i個物品時,不管上一次放沒放入過該物品;

dp[i-1][j]:不放入第i個物品時;

第一層迴圈i:0~n

第二層迴圈j:Ci~m,正向,因為不用顧忌每個物品只能放一次。

class Solution:
    """
    @param A: an integer array
    @param V: an integer array
    @param m: An integer
    @return: an array
    """
    def backPackIII(self, A, V, m):
        # write your code here
        n = len(A)
        if n <= 0 or m <= 0:
            return 0
        dp = [0 for _ in range(m+1)]
        for i in range(n):
            for j in range(A[i], m+1):
                dp[j] = max(dp[j-A[i]] + V[i], dp[j])
        return dp[-1]

小結1

0-1揹包問題 vs 完全揹包問題,實質性區別在於第二層迴圈一個是倒序,一個是正序:

4. 揹包問題IV / V —— 求方案數

0-1 揹包問題,返回方案數:https://www.lintcode.com/problem/backpack-v/description

完全揹包問題,返回方案數:https://www.lintcode.com/problem/backpack-iv/description

以完全揹包問題為例:

Given n items with size nums[i] which an integer array and all positive numbers, no duplicates. An integer target denotes the size of a backpack. Find the number of possible fill the backpack.

Each item may be chosen unlimited number of times

Example:

Given candidate items [2,3,6,7] and target 7,

A solution set is: 
[7]
[2, 2, 3]

return 2

解析:

將完全揹包問題裡面的max改為sum,且初始化時將dp[0] 設為1;

class Solution:
    """
    @param nums: an integer array and all positive numbers, no duplicates
    @param target: An integer
    @return: An integer
    """
    def backPackIV(self, nums, target):
        # write your code here
        n = len(nums)
        if n <= 0 or target < 0:
            return 0
        dp = [0 for _ in range(target+1)]
        dp[0] = 1
        for i in range(n):
            for j in range(nums[i],target+1):
                dp[j] = dp[j - nums[i]] + dp[j]
        return dp[-1]

5. 揹包問題VII —— 多重揹包問題

多重揹包問題,每個物品有限個個數,求最大價值:https://www.lintcode.com/problem/backpack-vii/description

問題描述:

Assume that you have n yuan. There are many kinds of rice in the supermarket. Each kind of rice is bagged and must be purchased in the whole bag. Given the weightprice and quantity of each type of rice, find the maximum weight of rice that you can purchase.

Example:

Given:
n = 8
prices = [2,4]
weight = [100,100]
amounts = [4,2]

Return:400

基本思路:

假設有N類物品,每個物品有M_i個,則可以轉換成 \sum_{i=1}^N M_i 個0-1揹包問題。則時間複雜度為:O(V \sum M ),,V為揹包容量,M是每個物品的個數的集合。

進階思路:

可以將每類物品分成 k = \left \lfloor log_2 M_i \right \rfloor + 1 個,每個對應一個新的價值和重量,然後轉化為\sum_{i=1}^N k_i個0-1 揹包問題,則時間複雜的降為:O(V \sum log_2M )

 

import math
class Solution:
    """
    @param n: the money of you
    @param prices: the price of rice[i]
    @param weight: the weight of rice[i]
    @param amounts: the amount of rice[i]
    @return: the maximum weight
    """
    def backPackVII(self, n, prices, weight, amounts):
        #將每一類物品轉化為k個子物品,求得對應的價錢,重量,轉化成0-1揹包問題
        #先生成新的物品集合
        new_prices = []
        new_weight = []
        for i in range(len(amounts)):
            k = int(math.log(amounts[i],2))+1
            coefs = [2**i for i in range(k - 1)]
            coefs.append(amounts[i] - 2**(k-1) + 1)
            for item in coefs:
                new_prices.append(item*prices[i])
                new_weight.append(item*weight[i])
        #0-1揹包問題
        dp = [0 for _ in range(n+1)]
        for i in range(len(new_prices)):
            for j in range(n, new_prices[i]-1, -1):
                dp[j] = max(dp[j-new_prices[i]] + new_weight[i], dp[j])
        return dp[-1]
    

6. 多重揹包可行性解

多重揹包問題求可行性解的個數:https://www.lintcode.com/problem/backpack-viii/description

問題描述:

Give some coins of different value and their quantity. Find how many values which are in range 1 ~ n can these coins be combined

Example

Given:
n = 10
value = [1,2,4]
amount = [2,1,1]

Return: 8
They can combine all the values in 1 ~ 8

思路: 

class Solution:
    """
    @param n: the value from 1 - n
    @param value: the value of coins
    @param amount: the number of coins
    @return: how many different value
    """
    #多重揹包可行性問題 O(VN)
    def backPackVIII(self, n, value, amount):
        # write your code here
        dp = [-1 for _ in range(n+1)]
        dp[0] = 0
        for i in range(len(value)):
            for j in range(n+1):
                if dp[j] >= 0:
                    dp[j] = amount[i]
                else:
                    dp[j] = -1
            for j in range(n-value[i]+1):
                if dp[j] > 0:
                    dp[j+value[i]] = max(dp[j+value[i]], dp[j] - 1)
            
        return n - dp.count(-1)

7. 換零錢問題

即完全揹包問題求解最小的使用個數。:https://www.lintcode.com/problem/coin-change/description

問題描述:

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example:

Given coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)

Given coins = [2], amount = 3
return -1.

Notice:

You may assume that you have an infinite number of each kind of coin.

思路:完全揹包問題,轉換成最小組合數。

class Solution:
    """
    @param coins: a list of integer
    @param amount: a total amount of money amount
    @return: the fewest number of coins that you need to make up
    """
    def coinChange(self, coins, amount):
        # write your code here
        dp = [float('inf') for _ in range(amount+1)]
        dp[0] = 0
        for i in range(len(coins)):
            for j in range(coins[i],amount+1):
                dp[j] = min(dp[j-coins[i]] + 1, dp[j])
        if dp[-1] < float('inf'):
            return dp[-1]
        else:
            return -1