代码随想录算法训练营第三十二天| 完全背包、 518. 零钱兑换 II、377. 组合总和 Ⅳ、70. 爬楼梯 (进阶)

写代码的第三十二天
开始完全背包了。。。。
01还没整明白

完全背包

思路

1、和01背包的第一个区别在于,他的每个物品都是可以重复使用的;
2、根据1的情况,代码中的两层for循环中的第二层循环在01背包的时候一直是倒叙的,为了只让每个物品用一次,那么在完全背包中就不用到序了,因为每个物品可以用多次,所以直接正序就可以了;
3、先遍历物品还是先遍历weight都可以!
简单来说就是01背包的二维情况是ij位置的value是由上方和左上方的值决定的,但是完全背包可以看作是只由左侧的数值决定。
正确代码:下面的主函数输入写了好久没写对,真无语啊。。。

def test_2_wei_bag_problem1(weight, value, bagWeight):
    dp = [0] * (bagWeight + 1)
    for i in range(len(weight)):  # 遍历物品
        for j in range(weight[i], bagWeight + 1):  # 遍历背包容量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
    return dp[bagWeight]

if __name__ == "__main__":
    # 输入研究材料的种类和行李空间
    N, V = map(int, input().split())
    
    weight = []
    value = []
    
    # 输入每种研究材料的重量和价值
    for _ in range(N):
        w, v = map(int, input().split())
        weight.append(w)
        value.append(v)

    result = test_2_wei_bag_problem1(weight, value, V)
    print(result)

518. 零钱兑换 II

思路

硬币数量都不止一个,所以是完全背包问题。
解决问题1:dp[j]代表什么?数组中的数字加和为j的时候有dp[j]种组合方式。
解决问题2:递推公式是什么?这个是组合的个数,所以可以回想爬楼梯以及昨天做的目标和这道题,所以最后的递推公式是dp[j] += dp[j-coins[i]],
解决问题3:dp数组的初始化是什么?和之前的题一样dp[0]的初始化为1(其实这里我是根据之前的题得出的,但是之前的题那个例子不太适用于这里)。我理解的dp[0]的定义是当背包容量为0的时候有1种方法,因为只有不符合条件的时候才返回值为0,所以只要有符合条件的时候就返回1。
解决问题4:遍历顺序是什么?如果先遍历物品后遍历weight,那么先遍历的是物品,那么物品一定是按照顺序放进去的,先是第一个,然后是第二个,那么就是组合,只有顺序[1,2,…];如果是先遍历weight后遍历物品的话,那么物品的顺序在每次遍历weight的时候都是从第一个开始的,所以顺序可以是[1,2]或者[2,1]。
解决问题5:输出dp数组。
正确代码:啊啊啊啊啊啊啊我自己写出来啦啦啦啦啦,别管是理解多少,先有答题思路了!!!!!!!好开心啊啊啊啊啊!!!!

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        dp = [0] * (amount + 1)
        dp[0] = 1
        for i in range(len(coins)):
            for j in range(coins[i],amount+1):
                dp[j] += dp[j-coins[i]]
        return dp[amount]

377. 组合总和 Ⅳ

思路

在本题中物品就是nums数组的下标,weight和value都是nums数组的值,target为背包容量。
需要注意!!!这个题是有顺序的!!!!!
解决问题1:dp[j]数组的含义是什么?满足背包容量为j的排列个数为dp[j]。
解决问题2:递推公式是什么?还是问组合情况的,还是考虑像爬楼梯那种递推公式,所以为dp[j] += dp[j-nums[i]]
解决问题3:dp数组初始化为什么?和之前的一样dp[0] =1
解决问题4:遍历顺序是什么?如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。

解决问题5:输出dp数组。
错误第一版:先遍历背包,背包容量0已经被定义的,所以要从1开始 ;习惯了先遍历物品后遍历背包,那么本题翻转过来就忘记了j-nums[i]这行代码是需要判断的,不然会越界的。

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        dp = [0] * (target + 1)
        dp[0] = 1
        for j in range(target+1):
            for i in range(len(nums)):
                dp[j] += dp[j-nums[i]]
        return dp[target]

正确代码

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        dp = [0] * (target + 1)
        dp[0] = 1
        for j in range(1,target+1):
            for i in range(len(nums)):
                if j >= nums[i]:
                    dp[j] += dp[j-nums[i]]
        return dp[target]

70. 爬楼梯 (进阶)

思路

之前的题是每次只能走一个台阶或者两个台阶,本题是每次可以最多走m个台阶,根据之前的递推公式可以知道dp[i] = dp[i-1]+dp[i-2],这里面的dp[i]数组代表走到i阶的时候有dp[i]种方法,1和2代表每次可以背包的重量,也就是第二层循环中代表weight的j,那么本题的dp数组定义也是一样的,所以dp[i] = dp[i-1]+dp[i-2]+…+dp[i-m]其实也就可以写作dp[i] +=dp[i-j];同时本题的先走1阶再走两阶和先走两阶再走一阶得到的路线方法是不同的,所以是排列,那么就应该先遍历weight,再遍历物品。
正确代码

def climbing_stairs(n,m):
    dp = [0] * (n + 1)
    dp[0] = 1
    for j in range(1,n+1):
        for i in range(1,m+1):
            if j >= i:
                dp[j] += dp[j-i]
    return dp[n]

if __name__ == '__main__':
    n,m = list(map(int,input().split(' ')))
    print(climbing_stairs(n,m))
  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值