美文网首页动态规划
LintCode-背包问题I、II、III、IV、V、VII、V

LintCode-背包问题I、II、III、IV、V、VII、V

作者: 想当厨子的程序员 | 来源:发表于2018-09-19 11:50 被阅读102次

    I

    描述

    在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]

    你不可以将物品进行切割。

    样例

    如果有4个物品[2, 3, 5, 7]

    如果背包的大小为11,可以选择[2, 3, 5]装入背包,最多可以装满10的空间。

    如果背包的大小为12,可以选择[2, 3, 7]装入背包,最多可以装满12的空间。

    函数需要返回最多能装满的空间大小。

    挑战

    O(n x m) time and O(m) memory.

    O(n x m) memory is also acceptable if you do not know how to optimize memory.

    代码

    体积计算值版本(超时)

    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 backPack(self, m, A):
            # write your code here
            dp = [False for j in range(m+1)]
            for a in A:
                for j in range(m, a-1, -1):
              # for j in reversed(range(a, m+1)):
                    dp[j] = max(dp[j], dp[j-a] + a)
            return dp[-1]
    

    两个for循环都超时,Time Limit Exceeded,

    for j in range(m, a-1, -1):
    for j in reversed(range(a, m+1)):
    

    说明了超时与reversed和range的叠加使用没有关系。这个算法的java代码是可以过的,python通过不了,说明python在数值计算方面的性能还是不及java的。

    True Or False版本

    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 backPack(self, m, A):
            # write your code here
            dp = [False for j in range(m+1)]
            dp[0] = True
            result = 0
            for a in A:
                for j in range(m, a-1, -1):
                    if dp[j-a] == True:
                        dp[j] = True
                        if j > result:
                            result = j
            return result
    

    使用result记录能达到的最大result,用True Or False来说明上个体积状态是否可以达到,就不会超时。证明了True Or False的赋值过程比起体积的计算赋值在python中性能更好更省时间。

    题目来源

    https://www.lintcode.com/problem/backpack/description

    II

    描述

    给出n个物品的体积A[i]和其价值V[i],将他们装入一个大小为m的背包,最多能装入的总价值有多大?

    A[i], V[i], n, m均为整数。你不能将物品进行切分。你所挑选的物品总体积需要小于等于给定的m。

    样例

    对于物品体积[2, 3, 5, 7]和对应的价值[1, 5, 2, 4], 假设背包大小为10的话,最大能够装入的价值为9。

    挑战

    O(n x m) memory is acceptable, can you do it in O(m) memory?

    代码

    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
            dp = [0 for j in range(m+1)]
            
            for i in range(1, len(A)+1):
                for j in reversed(range(1, m+1)):
                    if j < A[i-1]:
                        pass
                    else:
                        dp[j] = max(dp[j], dp[j-A[i-1]] + V[i-1])
            return dp[-1]
    

    题目来源

    https://www.lintcode.com/problem/backpack-ii/description

    III

    描述

    给定n种具有大小 Ai 和价值 Vi 的物品(每个物品可以取用无限次)和一个大小为 m 的一个背包, 你可以放入背包里的最大价值是多少?

    样例

    给出四个物品, 大小为 [2, 3, 5, 7], 价值为 [1, 5, 2, 4], 和一个大小为 10 的背包. 最大的价值为 15.

    注意事项

    你不能将物品分成小块, 选择的项目的总大小应 小于或等于 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
            if len(A) == 0:
                return 0
            dp = [0 for j in range(m + 1)]
    
            for j in range(1, m + 1):
                for k in range(0, len(A)):
                        if A[k] <= j:
                            dp[j] = max(dp[j], dp[j - 1], dp[j - A[k]] + V[k])
            return dp[-1]
    

    题目来源

    https://www.lintcode.com/problem/backpack-iii/description

    IV

    描述

    给出 n 个物品, 以及一个数组, nums[i]代表第i个物品的大小, 保证大小均为正数并且没有重复, 正整数 target 表示背包的大小, 找到能填满背包的方案数。
    每一个物品可以使用无数次

    样例

    给出候选物品集合 [2,3,6,7] 和 target 7,

    一个答案集合为:

    [7]
    [2,2,3]
    返回 2

    代码

    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
            dp = [[0 for j in range(target+1)] for i in range(len(nums))]
            for j in range(target+1):
                if j%nums[0] == 0:
                    dp[0][j] = 1
            for i in range(1, len(nums)):
                for j in range(0, target+1):
                    if j >= nums[i]:
                        k = j - nums[i]
                        while (k >= 0):
                            dp[i][j] += dp[i - 1][k]
                            k = k - nums[i]
                    dp[i][j] += dp[i-1][j]
            return dp[-1][-1]
    

    题目来源

    https://www.lintcode.com/problem/backpack-iv/description

    V

    描述

    给出 n 个物品, 以及一个数组, nums[i] 代表第i个物品的大小, 保证大小均为正数并且没有重复, 正整数 target 表示背包的大小, 找到能填满背包的方案数。
    每一个物品只能使用一次

    样例

    给出候选物品集合 [1,2,3,3,7] 以及 target 7

    结果的集合为:

    [7]
    [1,3,3]
    返回 2

    代码

    初始版(超时)

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

    但是超时, Time Limit Exceeded, for j in range(1, target+1)浪费了很多无用的时间。

    优化时间版(超内存)

    class Solution:
        """
        @param nums: an integer array and all positive numbers
        @param target: An integer
        @return: An integer
        """
        def backPackV(self, nums, target):
            # write your code here
            dp = [[0 for j in range(target+1)] for i in range(len(nums))]
            for i in range(len(nums)):
                dp[i][0] = 1
            dp[0][nums[0]] = 1
            Sum = nums[0]
            for i in range(1, len(nums)):
                Sum += nums[i]
                for j in range(1, min(Sum+1, target+1)):
                    if j >= nums[i]:
                        dp[i][j] += dp[i - 1][j-nums[i]]
                    dp[i][j] += dp[i-1][j]
            return dp[-1][-1]
    

    这个版本不超时了,但是超内存使用, Memory Limit Exceeded, 说明对于一些case,

    dp = [[0 for j in range(target+1)] for i in range(len(nums))] 
    

    开的太大了。

    优化内存使用版本A(超时)

    class Solution:
        """
        @param nums: an integer array and all positive numbers
        @param target: An integer
        @return: An integer
        """
        def backPackV(self, nums, target):
            # write your code here
            dp = [[0 for j in range(target+1)] for i in range(2)]
            dp[0][0] = 1
            dp[0][nums[0]] = 1
            Sum = nums[0]
            for i in range(1, len(nums)):
                Sum += nums[i]
    
                for j in range(0, min(Sum+1, target+1)):
                    if j >= nums[i]:
                        dp[1][j] += dp[0][j - nums[i]]
                    dp[1][j] += dp[0][j]
                for j in range(0, min(Sum+1, target+1)):
                    dp[0][j] = dp[1][j]
                    dp[1][j] = 0
            return dp[0][-1]
    

    两行dp循环来使用,但又开始超时了, Time Limit Exceeded,

    for j in range(0, min(Sum+1, target+1)):
                    dp[0][j] = dp[1][j]
                    dp[1][j] = 0
    

    估计是循环赋值增加了一倍时间导致的。

    优化内存使用版本B(错误)

    class Solution:
        """
        @param nums: an integer array and all positive numbers
        @param target: An integer
        @return: An integer
        """
        def backPackV(self, nums, target):
            # write your code here
            dp = [0 for j in range(target+1)]
            dp[0] = 1
            Sum = nums[0]
            for i in range(1, len(nums)):
                Sum += nums[i]
                for j in range(1, min(Sum+1, target+1)):
                    if j >= nums[i]:
                        dp[j] += dp[j - nums[i]]
            return dp[-1]
    

    使用一行dp来完成,正向遍历,

    for j in range(1, min(Sum+1, target+1)):
    

    每次都把上一次的累计方法覆盖掉,但是实际上这是错误的解法, 因为每次更新某个j的状态时涉及到的j-nums[i]状态可能会把自己再使用一次。

    优化内存使用版本C(正确)

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

    最好也是最正确的解法,在优化内存使用版本B的基础上倒过来遍历j就不会把当前nums[i]重复使用了,因为先更新j大的状态, 则更新后面j大的状态时,j-nums[i]<j的状态都还是上一个i的状态。

    题目来源

    https://www.lintcode.com/problem/backpack-v/description

    VII

    描述

    假设你身上有 n 元,超市里有多种大米可以选择,每种大米都是袋装的,必须整袋购买,给出每种大米的重量,价格以及数量,求最多能买多少公斤的大米

    样例

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

    Return:400

    代码

    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):
            # write your code here
            dp = [0 for j in range(n+1)]
            
            for i in range(0, len(prices)):
                for k in range(0, amounts[i]):
                    for j in range(n, prices[i]-1, -1):
                        dp[j] = max(dp[j], dp[j-prices[i]]+weight[i])
            return dp[-1]                    
    

    轻松过,很OK。

    题目来源

    https://www.lintcode.com/problem/backpack-vii/description

    VIII

    给一些不同价值和数量的硬币。找出这些硬币可以组合在1 ~ n范围内的值

    样例

    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
        """
        def backPackVIII(self, n, value, amount):
            # write your code here
            dp = [False for j in range(n+1)]
            result = 0
            dp[0] = True
            for i in range(0, len(value)):
                cnt = [0 for j in range(n+1)]
                for j in range(value[i], n+1):
                    if dp[j] is not True and dp[j-value[i]] is True and cnt[j-value[i]] < amount[i]:
                        dp[j] = dp[j-value[i]]
                        result += 1
                        cnt[j] = cnt[j-value[i]] + 1
            return result
    

    cnt用来记录每套相同价值的多数量硬币的使用次数,需要在这里特别说明的是此问题对比背包问题I, 因为他们都是计算通过True和False的方式来记录是否能达到背包空间使用了j数量,但用法有特异性。
    背包问题I为了节省空间,dp是一维数组,并且j是从后往前遍历的,为什么?
    因为如果从前往后遍历,当j=2*value[i]时,j-value[i]=value[i],那么同一个石头就被用使用两次,而题目定义中石头仅有一个,所以就错误了。
    而背包问题VII中的j只能从前往后遍历,为什么?
    因为只有从前往后遍历,cnt才能通过j-value[i]状态的数值知道当前j状态的数值,那么为什么不会错误,因为这个题目定义中某一类硬币是有多个的, 并且cnt[j-value[i]]会和amount[i]做对比,保证硬币的使用是合乎逻辑,也就是足够用的。

    题目来源

    https://www.lintcode.com/problem/backpack-viii/description

    IX

    你总共有10 * n 千元(n万元 ),希望申请国外的大学,要申请的话需要交一定的申请费用,给出每个大学的申请费用以及你得到这个大学offer的成功概率,大学的数量是 m。如果经济条件允许,你可以申请多所大学。找到获得至少一份工作的最高可能性。

    样例

    给定:
    n = 10
    prices = [4,4,5]
    probability = [0.1,0.2,0.3]

    返回:0.440

    注意事项

    0<=n<=10000,0<=m<=10000

    代码

    class Solution:
        """
        @param n: Your money
        @param prices: Cost of each university application
        @param probability: Probability of getting the University's offer
        @return: the  highest probability
        """
        def backpackIX(self, n, prices, probability):
            # write your code here
            for i in range(len(probability)):
                probability[i] = 1 - float(probability[i])
                
            dp = [1 for j in range(n+1)]
            
            for i in range(1, len(prices)+1):
                for j in range(n, prices[i-1]-1, -1):
                    dp[j] = min(dp[j], dp[j-prices[i-1]]*probability[i-1])
            return 1-dp[-1]
    

    我感觉这个题目的case有些简单。

    题目来源

    https://www.lintcode.com/problem/backpack-ix/description

    X

    你总共有n元,商人总共有三种商品,它们的价格分别是150元,250元,350元,三种商品的数量可以认为是无限多的,购买完商品以后需要将剩下的钱给商人作为小费,求最少需要给商人多少小费

    样例

    给定: n = 900
    返回: 0

    代码

    class Solution:
        """
        @param n: the money you have
        @return: the minimum money you have to give
        """
        def backPackX(self, n):
            # write your code here
            dp = [False for j in range(n+1)]
            dp[0] = True
            result = 0
            for money in (150, 250, 350):
                for j in range(money, n+1):
                    if dp[j] is not True and dp[j-money] is True:
                        dp[j] = True
                        if j > result:
                            result = j
            return n-result
    

    简单题目,使用True or False最节省时间空间,可以参考背包问题I。

    for j in range(money, n+1):
    

    一定要从money而不是1开始,否则j-money<0会使得dp[j-money]出现dp[-n]的结果。

    题目来源

    https://www.lintcode.com/problem/backpack-x/description

    相关文章

      网友评论

        本文标题:LintCode-背包问题I、II、III、IV、V、VII、V

        本文链接:https://www.haomeiwen.com/subject/etabnftx.html