开个帖子把动态规划的模板题先写了
合集地址:牛客动态规划模版题
6 月 7 日
完全背包
代码
说下思路吧
这里我们定义一个一维的 dp 数组,长度为 V + 1(V是体积)
dp[j] 就代表背包体积 j 所能容纳的最大物品价值总和
对于每个物品 i,我们只有加或者不加两种操作:
- 加,那么就是 dp[ j - v[ i ] ] + w[ i ]
- 不加,那么就是 dp[ j ]
(1)求至多能装多大?
这个只管装就是了,那么我们外层循环遍历体积,内层循环遍历物品
( PS:外层循环遍历物品,内层循环遍历体积也行)
如何理解呢?
其实就是可以看作是,我们对于每个 j 体积的背包,都会去遍历所有的物品,看能不能多装一点,而且这也利用到了物品数量不限制的条件
举个例子:
我背包体积为 8,有体积为 6,4,2的三个物品,为了简化,不讨论物品的价值,毕竟价值就是一个取 max 的处理;
-> 当 v 从 0,1,……,8 依次考虑时候,当 v = 4 的时候,也就是计算 dp [4]:
- 我们逐个遍历物品,6 放不下,4 刚好放下,那么就是 dp[4] = dp[4 - v[ i ]] + w[i],这里 物品 i 指的就是体积为 4 的这个物品;
- 接着我们遍历物品三,dp[4] = dp[4 - v[i]] + w[i] = dp[4 - 2] + w[ i ] = dp[2] + w[i]
- dp[2]已经计算过了,也就是说体积为 4 的背包有两种方案:1️⃣ 4 、 2️⃣ 2 + 2
所以这就是为什么说 外层循环遍历体积,内层循环遍历物品,可以利用到物品数量不限制的条件,这也就是完全背包和 01 背包不一样的地方。
至于外层循环遍历物品,内层循环遍历体积,可以这么理解:
- 当只有物品一这种规格的时候,我装体积 V 的背包,可以算出来装多少;
- 然后物品增加到有两样物品的时候(我已经有了物品一能装出来的最大价值),那么考虑放入物品 2 会不会增加装的价值;
- 以此类推,我们有 3 种、 4 种、 5 种……物品规格的时候
(2) 体积为 V,求恰好能装多大?
这里有个不一样的就是,比如背包体积为 8,仍然是有体积为 6,4,2的三个物品:
题一中,dp[5]是有值的,也就是之前两种方案的最大值;
而在本问中,dp[5]是为 0 的,因为这三种物品,你是凑不到体积刚好为 5 的,那么怎么把这个约束加进去呢?
我觉得这个处理逻辑可以好好思考写一下!
# 最多装多少价值
def getMaxW1(arrV, arrW, V):
# 定义 dp 数组,dp[j]表示体积j 情况下容量的最大价值
dp =[0] + [0] * V
for i in range(len(arrV)):
for j in range(arrV[i], V + 1):
# 当前物品装或者不装的最大值
dp[j] = max(dp[j - arrV[i]] + arrW[i], dp[j])
return dp[V]
# 恰好装满时是多少价值
def getMaxW2(arrV, arrW, V):
# 定义 dp 数组,dp[j]表示体积j 情况下容量的最大价值
dp =[0] + [0] * V
for j in range(V + 1):
for i in range(len(arrV)):
# 当前物品装或者不装的最大值
if j >= arrV[i]:
if j - arrV[i] == 0 or dp[j - arrV[i]] != 0:
dp[j] = max(dp[j - arrV[i]] + arrW[i], dp[j])
# 下面的不要
# else:
# dp[j] = dp[j]
# print(dp)
return dp[V]
if __name__ == "__main__":
n, V = map(int, input().split())
arrW = []
arrV = []
for _ in range(n):
v, w = map(int, input().split())
arrV.append(v)
arrW.append(w)
print(getMaxW1(arrV,arrW,V))
print(getMaxW2(arrV,arrW,V))
(PS:虽然之前在 leetcode 上写过,但是写第二问的时候,还是出了好多错,debug 了挺久的,还是不熟练啊,愈发觉得自己菜😭)
6 月 8 日
01 背包:题目地址
代码
# 老流畅,ACM 模式处理输入
n, V = map(int, input().split())
vList = []
wList = []
for _ in range(n):
vi, wi = map(int, input().split())
vList.append(vi)
wList.append(wi)
# 第一问
def getMax1(vList, wList, V):
dp = [0] + [0] * V
for i in range(len(vList)):
for j in range(V, vList[i] - 1, -1):
dp[j] = max(dp[j - vList[i]] + wList[i], dp[j])
return dp[V]
# 第二问
def getMax2(vList, wList, V):
dp = [0] + [float('-inf')] * V
for i in range(len(vList)):
for j in range(V, vList[i] - 1, -1):
dp[j] = max(dp[j - vList[i]] + wList[i], dp[j])
return dp[V] if dp[V] != float('-inf') else 0
print(getMax1(vList, wList, V))
print(getMax2(vList, wList, V))
- 第一问
没什么好说的,主要就是要体积要倒序遍历,不然会存在物品复用(因为这里说了每个物品只有一件)
- 第二问
给我整的有点懵,直接倒序有问题,一开始没想出该做哪里的修改;
- 一开始也是倒序,然后发现不知道恰好体现在哪里,然后开始定义dp[ j ],表示背包还剩体积 j所恰好装的,然后返回判断 dp[0],最后也没做出来
(PS:估计这里定义的还是还剩体积 j所能装的最大容量而不是恰好)
- 最后其实还是要改个初始化的值,这样就可以判断恰好,因为这相当于是强制从 体积为 0 开始装起(如果全部初始化为 0,那么就相当于可以从任意位置开始装,就意味着会有些位置是空,即没装满)
6月 9 日
二维差分
代码
第一次提交过了 88.3%,超时了也是在意料之中吧,毕竟纯暴力
💡PS:先写一维差分再去写二维差分可能更好理解一点
def updated_grid(x1, y1, x2, y2, k, grid):
i= x1 - 1
while i < x2:
j = y1 - 1
while j < y2:
grid[i][j] += k
j += 1
i += 1
# print(grid)
return grid
if __name__ == '__main__':
# 1. input
n, m, q = map(int, input().split())
grid = []
for _ in range(n):
grid.append(list(map(int, input().split())))
for i in range(q):
x1, y1, x2, y2, k = map(int, input().split())
# 2. updated grid
grid = updated_grid(x1, y1, x2, y2, k, grid)
# 3. print result
for i in range(n):
for j in range(m):
if j != m - 1:
print(f"{grid[i][j]} ", end="")
else:
print(grid[i][j])
第一次二维差分 DP,但是看起来怎么这么抽象呢?晚点再写吧,累了
6 月 12 日
前几天在搞博客,写了一个定时 tracking diffusion paper 然后更新的主页的自动化流程,
所以没定时打卡……
二维前缀和
代码
备注这个数据读写时间注意,没考虑太懂怎么注意⚠️
先硬写一版代码(一次性过了)
n, m, q = map(int, input().split())
nums = []
# 输入
for _ in range(n):
nums.append(list(map(int, input().split())))
"""
定义前缀和数组:
prefixSum[i][j], 代表以 [i][j]为右下角,[0][0]为左上角的数组和的值
"""
prefixSum = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, m + 1):
prefixSum[i][j] = prefixSum[i - 1][j] + prefixSum[i][j - 1] - prefixSum[i - 1][j - 1] + nums[i - 1][j - 1]
for _ in range(q):
x1, y1, x2, y2 = map(int, input().split())
ans = prefixSum[x2][y2] - prefixSum[x2][y1 - 1] - prefixSum[x1 - 1][y2] + prefixSum[x1 - 1][y1 - 1]
print(ans)
先写一维差分再去写二维差分可能更好理解一点
差分
代码
不会写的看看这个博客:每周一算法:差分算法-CSDN博客
然后从左往右边开始累计加和,就出来结果了
n, m = map(int, input().split())
nums = list(map(int, input().split()))
# 最后加一个 0 是为了防止溢出
diffnums = [0] * len(nums) + [0]
for _ in range(m):
l, r, k = map(int, input().split())
diffnums[l - 1] += k
diffnums[r] -= k
for i in range(1, n + 1):
diffnums[i] += diffnums[i - 1]
for i in range(n):
nums[i] += diffnums[i]
print(' '.join(map(str,nums)))
6 月 18 日
前缀和
代码
n, q = map(int, input().split())
nums = list(map(int, input().split()))
nums = [0] + nums
for i in range(1, len(nums)):
nums[i] += nums[i - 1]
for _ in range(q):
l, r = map(int, input().split())
print(nums[r] - nums[l - 1])