背包问题(python)

01背包

先浅浅的占个位置,接下来的三天内补上

         给定一个容积为V的背包,现在有N个物品,第i件物品的体积为wi,价值为vi。每件物品只能拿或者不拿,请求出体积总和不超过V的最大价值。

动态规划状态:dp[i][j]前i件物品,体积为j的最大价值。背包问题通用的用法

状态转移方程:

边界问题:dp[...][j] = 0, dp[...][0] = 0,直接在初始化时解决。

直接看一个模版题目:

        在这里最困难的是状态转移方程,01背包的状态转移方程上面已经给出。代码比较简单,就不加注释了。

n, v = map(int, input().split())

dp = [[0] * (v + 1) for _ in range(n + 1)]

for i in range(1, n + 1):
    wi, vi = map(int,input().split())
    for j in range(1, v + 1):
        if j - wi >= 0:
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - wi] + vi)
        else:
            dp[i][j] = dp[i - 1][j]
print(dp[n][v])

滚动数组优化:

        上述的方程中,第i行的更新,仅和第i - 1行有关, 因此可以使用滚动数组优化 。 

优化代码只需要在第一维对2取模即可。   

n, v = map(int, input().split())

dp = [[0] * (v + 1) for _ in range(2)]

for i in range(1, n + 1):
    wi, vi = map(int,input().split())
    for j in range(1, v + 1):
        if j - wi >= 0:
            dp[i % 2][j] = max(dp[(i - 1) % 2][j], dp[(i - 1) % 2][j - wi] + vi)
        else:
            dp[i % 2][j] = dp[(i - 1) % 2][j]
print(dp[n % 2][v])

进一步的优化使用长度为1的滚动数组优化可空间:

        通过观察可以发现:在更新当前dp[i][j]是需上一横行前面的值即可,因此只需要在第二层循环时反着来即可。(可能刚开始不好理解,可以画图,尝试理解一下)。

n, v = map(int, input().split())

dp = [0] * (v + 1)

for i in range(1, n + 1):
    wi, vi = map(int, input().split())
    # 在这里一定要反着更行,注意取值。
    for j in range(v, wi - 1, -1):
        
        dp[j] = max(dp[j], dp[j - wi] + vi)
print(dp[v])

链接:倒数

        刚开始做这个题时很蒙,不知道为什么,dp[j] = dp[j] + ei,没有限制条件 if ai > j: ,后来看dp[i][j]的含义的时候才发现问题所在,dp[i][j]:的含义是前i个人总共倒水为j。哎呀我描述不清楚,大家自行理解吧。

# dp[i][j]:表示前i个人的总共倒水为j
n, m = map(int, input().split())
dp = [0] * (m + 1)
for i in range(1, n + 1):
    ai, bi, ci, di, ei = map(int, input().split())
    # 题目中说倒水不足ai毫升(可以为0)注意:这个可以为0的条件。
    for j in range(m, -1, -1):
        # 在以下的条件中更新最大的dp[j]和01背包类似只是多了一部分的条件
        dp[j] = dp[j] + ei
        if j - ai >= 0:
            dp[j] = max(dp[j], dp[j - ai] + bi)
        if j - ci >= 0:
            dp[j] = max(dp[j], dp[j - ci] + di)
print(dp[m])

盗墓分赃

import sys

n = int(input())

a = [int(input()) for _ in range(n)]
# m:表示宝物的总质量
m = sum(a)
# 将总质量分半
v = m // 2

dp = [0] * (v + 1)
if m & 1:
    print('no')
    sys.exit()
for i in range(n):
    wi, vi = a[i], a[i]
    for j in range(v, wi - 1, -1):
        dp[j] = max(dp[j], dp[j - wi] + vi)
if dp[v] == v:
    print('yes')
else:
    print('no')

蓝桥课程抢购

n = int(input())

# dp[i][j]:表示前i个课程,等待时间为j
dp = [0] * (int(1e5) + 10)
li = []
# 这里为什么排序我也有点蒙蒙的
for i in range(n):
    li.append(list(map(int, input().split())))
li.sort(key=lambda x: (x[1]))

for i in range(0, n):
    # a, b, c = map(int, input().split())
    a, b, c = li[i][0], li[i][1], li[i][2]
    # 对于这里要注意b,打折截止时间,如果截止就没有办法选课了。所以注意范围
    for j in range(b, a - 1, -1):
        dp[j] = max(dp[j], dp[j - a] + c)
print(max(dp))
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值