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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,719评论 0 33
  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,320评论 0 2
  • 回溯算法 回溯法:也称为试探法,它并不考虑问题规模的大小,而是从问题的最明显的最小规模开始逐步求解出可能的答案,并...
    fredal阅读 13,623评论 0 89
  • 01 上周六,朋友L开车送我回家。 途经湘江边在路口等红灯时,他侧过脸望着江堤说“也是这个季节,就在这个地方,八年...
    叁度先生阅读 797评论 0 3
  • 3.1日 周2 写作NO.18 昨天是繁忙的周一。 每次读完一本书,我都会在微信朋友圈里发书摘,书给我的启示,以及...
    烟紫雪阅读 391评论 0 0