牛客刷题合集-动态规划系列(一)

开个帖子把动态规划的模板题先写了

合集地址:牛客动态规划模版题


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])


  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值