笔试考点整理---背包问题

绝大部分参考 https://blog.csdn.net/u013166817/article/details/85449218
非商用仅用于笔者笔试知识点整理学习。

0-1背包问题,无价值

0-1背包也是背包问题的最常见的两种问法:

一是要求“恰好装满背包”时的最优解
二是“没有要求必须把背包装满”。

这两种问法的实现方法不同点主要在初始化上。

如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞(有时是+),这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为0。
正无穷 float(“inf”) 负无穷 float("-inf")
"""
@param m: An integer m denotes the size of a backpack
@param A: Given n items with size A[i]
@return: The maximum size最大装满多少size
倒序是因为每个物品只能用一次。
"""

n = len(A)
if n <= 0 or m <= 0:
    return 0
dp = [0]*(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])

print(dp[-1])

0-1背包问题,有价值

"""
@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 size
"""

n = len(A)
if n <= 0 or m <= 0:
    return 0
dp = [0]*(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])

print(dp[-1])

完全背包问题 有价值无限个数

"""
@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 size
"""

n = len(A)
if n <= 0 or m <= 0:
    return 0
dp = [0]*(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])

print(dp[-1])

0-1背包问题,有价值 求找到可能装满背包的方案数

"""
@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 size
"""

n = len(A)
if n <= 0 or m <= 0:
    return 0
dp = [0]*(m+1)
dp[0]=1
for i in range(n):
    for j in range(m,A[i]-1,-1):#倒序重点 防止覆盖
        dp[j] = dp[j-A[i]] +dp[j]

print(dp[-1])

完全背包问题 有价值无限个数 求找到可能装满背包的方案数

"""
@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 size
将完全背包问题里面的max改为sum,且初始化时将dp[0] 设为1;
"""

n = len(A)
if n <= 0 or m <= 0:
    return 0
dp = [0]*(m+1)
dp[0]=1
for i in range(n):
    for j in range(A[i], m+1):#正序
        dp[j] = dp[j-A[i]] +dp[j]

print(dp[-1])

多重背包问题

最优思路可以将每类物品分成 k = ⌊ l o g 2 M i ⌋ + 1 k = \left \lfloor log_2 M_i \right \rfloor + 1 k=log2Mi+1 个,每个对应一个新的价值和重量,然后转化为 ∑ i = 1 N k i \sum_{i=1}^N k_i i=1Nki个0-1 背包问题,则时间复杂的降为: O ( V ∑ l o g 2 M ) O(V \sum log_2M ) O(Vlog2M)

"""
import math

"""
@param n: the money of you
@param prices: the price of rice[i] 价值
@param weight: the weight of rice[i]size
@param amounts: the amount of rice[i]数量
@return: the maximum weight
"""
    
#将每一类物品转化为k个子物品,求得对应的价钱,重量
#1,2,4,…,2**(k-1),n[i]-2**k+1,
#转化成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])


print(dp[-1])


也可以这样 但是复杂度高一些

import math#  math.floor()和int()一样都可以向下取整
#math.ceil( x )向上取整
V,k=map(int,input().split())
N=[]
Q=[]
value=[]
for i in range(k):
    n,q,v=map(int,input().split())
    N.append(n)
    Q.append(q)
    value.append(v)
L=len(Q)
dp=[0]*(V+1)
for i in range(L): #物品
    for j in range(V,Q[i]-1,-1): #容积
        for k in range(1,min(N[i],math.floor(j/Q[i]))+1):
            dp[j]=max(dp[j-k*Q[i]]+k*value[i],dp[j])

多重背包可行性解

给一些不同价值和数量的硬币。找出这些硬币能组合多少个在1~n范围内的值 当然也可以判断能不能凑成n

"""
@param n: the value from 1 - n  背包容量n
@param value: the value of coins 价值
@param amount: the number of coins 数量
@return: how many different value
"""
#多重背包可行性问题 O(VN)

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]
        elif j < value[i] or dp[j - value[i]] <= 0:
             dp[j] = -1
        else:
             dp[j] = dp[j - value[i]] - 1
     
print(n - dp.count(-1))

换零钱问题

即完全背包问题求解最小的使用个数

"""
@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 
"""
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

https://blog.csdn.net/u013166817/article/details/85449218

你可能感兴趣的:(笔试考点整理---背包问题)