【日常系列】LeetCode《27·动态规划2》

这篇具有很好参考价值的文章主要介绍了【日常系列】LeetCode《27·动态规划2》。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

数据规模->时间复杂度

<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)

内容

1)爬楼梯、打家劫舍问题
2)0-1,多重,完全,二维被动背包问题

lc 70【剑指 10 - 2】【top100】: 爬楼梯
https://leetcode.cn/problems/climbing-stairs/
提示:
1 <= n <= 45
【日常系列】LeetCode《27·动态规划2》

#方案一:dfs+记忆化
class Solution:
    def climbStairs(self, n: int) -> int:
        memo=[-1]*(n+1)
        def dfs(n):
            if n==1:return 1
            if n==2:return 2
            if memo[n]!=-1:return memo[n]
            #
            memo[n]=dfs(n-1)+dfs(n-2) #left+right
            return memo[n]
        return dfs(n)

#方案二:dp+压缩
class Solution:
    def climbStairs(self, n: int) -> int:
        dp=[-1]*(n+1) #走到i台阶,对应的方法数量
        dp1,dp2=1,2
        #[n-1]+1->[n],[n-2]+2->[n]
        #(注意([n-1]+1)已存在[n-2],所以[n-2]+1+1等价于[n-2]+2)
        for i in range(3,n+1):
            dp1=dp2
            dp2=dp1+dp2
        return dp2   

lc 746【剑指 088】:使用最小花费爬楼梯
https://leetcode.cn/problems/min-cost-climbing-stairs/
提示:
2 <= cost.length <= 1000
0 <= cost[i] <= 999
【日常系列】LeetCode《27·动态规划2》

#方案一:回溯+dfs
class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        n=len(cost)
        memo=[-1]*(n+1)
        def dfs(i):
            if i==0 or i==1:return 0 #从下标为 0 或下标为 1 的台阶开始爬楼梯,不需要体力值,只是从0->1或者0->2,需要cost[i]体力值
            if memo[i]!=-1:return memo[i]
            #
            memo[i]=min(dfs(i-1)+cost[i-1],dfs(i-2)+cost[i-2])#表示爬到i-1的最低花费+cost[i-1]->爬到i
            return memo[i]
        return dfs(n)
#方案二:dp+压缩
class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        n=len(cost)
        prev=curr=0
        for i in range(2,n+1):
            tmp=curr   
            curr=min(curr+cost[i-1],prev+cost[i-2])
            prev=tmp       
        return curr       

lc 198【剑指 089】【top100】:打家劫舍
https://leetcode.cn/problems/house-robber/
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400
【日常系列】LeetCode《27·动态规划2》
【日常系列】LeetCode《27·动态规划2》

#方案一:回溯+记忆化
#写法一
class Solution:
    def rob(self, nums: List[int]) -> int:
        n=len(nums)
        memo=[-1]*n # memo[i]:偷盗[i, nums.length - 1],包括i户(偷/不偷),区间房子得到的最大金额
        def dfs(i):
            if i>=n:return 0
            if memo[i]!=-1:return memo[i] 
            #
            memo[i]=max(dfs(i+1),dfs(i+2)+nums[i])
            return memo[i]
        return dfs(0)
#写法二
class Solution:
    def rob(self, nums: List[int]) -> int:
        n=len(nums)
        memo=[-1]*n
        def dfs(i):
            if i<0:return 0
            if memo[i]!=-1:return memo[i] #偷到i户,包括i户(偷/不偷),所能获得的最大金额
            #
            memo[i]=max(dfs(i-1),dfs(i-2)+nums[i])
            return memo[i]
        return dfs(n-1)

#方案二:dp+压缩
class Solution:
    def rob(self, nums: List[int]) -> int:
        n=len(nums)
        if n==1:return nums[0]
        
        dp=[-1]*n
        prev=curr=0
        
        for i in range(n):
            tmp=curr
            curr=max(prev+nums[i],curr)
            prev=tmp
        return curr

lc 213【剑指 090】:打家劫舍 II
https://leetcode.cn/problems/house-robber-ii/
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
【日常系列】LeetCode《27·动态规划2》

class Solution:
    def rob(self, nums: List[int]) -> int:
        n=len(nums)
        if n==1:return nums[0] 
        def rob1(i,j):  
            dp=[-1]*n
            #
            prev=curr=0
            for k in range(i,j+1):
                tmp=curr
                curr=max(prev+nums[k],curr)
                prev=tmp
            return curr
        return max(rob1(0,n-2),rob1(1,n-1)) #key:

lc 337【top100】:打家劫舍 III
https://leetcode.cn/problems/house-robber-iii/
提示:
树的节点数在 [1, 10^4] 范围内
0 <= Node.val <= 10^4
【日常系列】LeetCode《27·动态规划2》

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def rob(self, root: Optional[TreeNode]) -> int:
        def dfs(node):
            if not node:return [0,0]
            #
            left,right=dfs(node.left),dfs(node.right)
            #key:1)当不考虑当前节点,则在子节点中寻找到最优组合分(key-key-key)
            #2)当考虑当前节点时,则累加在不选子节点的情况下,获得的最优分
            res=[0]*2
            res[0]=max(left[0],left[1])+max(right[0],right[1])
            res[1]=left[0]+right[0]+node.val
            return res
        ans=dfs(root)
        return max(ans[0],ans[1])

0 - 1 背包问题:每种背包只能取一次
【日常系列】LeetCode《27·动态规划2》
【日常系列】LeetCode《27·动态规划2》
【日常系列】LeetCode《27·动态规划2》
【日常系列】LeetCode《27·动态规划2》

#方案一:dfs-前序
def knapsack01(w, v, C):
    maxValue = float('-inf')
    def dfs(start,remain_w,curr_value):
        nonlocal maxValue
        maxValue=max(maxValue,curr_value) #处理当前节点
        #处理子节点
        for i in range(start,len(w)):
            child_i=i+1
            #剪枝
            if child_i==len(w):continue
            if remain_w-w[child_i]<0:continue
            dfs(child_i,remain_w-w[child_i],curr_value+v[child_i])
    dfs(-1, C, 0)
    return maxValue
print(knapsack01([3,4,5],[15,14,12],10))

#方案二:dfs-后序
def knapsack01(w, v, C):
    def dfs(start,remain_w):
        maxValue = 0 #key:位置,最底子节点maxValue从0开始
        #
        for i in range(start,len(w)):
            child_i=i+1
            if child_i==len(w):continue
            if remain_w-w[child_i]<0:continue
            #
            childmaxValue=dfs(child_i, remain_w - w[child_i])
            maxValue=max(maxValue,childmaxValue)  #考虑到,返回各子分支中的最大值

        k=0 if start==-1 else v[start]
        return maxValue+k

    return dfs(-1, C)
print(knapsack01([3,6,7],[15,10,11],10))

#方案三:dp
def knapsack01(w, v, c):
    dp=[[0]*(c+1) for _ in range(len(w))]

    for j in range(c+1):
        if j>=w[0]:dp[0][j]=v[0]

    for i in range(1,len(w)):
        for j in range(c+1):
            if j<w[i]:dp[i][j]=dp[i-1][j]
            else:dp[i][j]=max(dp[i-1][j],v[i]+dp[i-1][j-w[i]])
    return dp[len(w)-1][c]

print(knapsack01([3,6,7],[15,14,13],10))

#方案四:dp+状态压缩(注意状态转移方向)【重】

def knapsack01(w, v, c):
    dp=[0]*(c+1)

    #for j in range(c+1): #可以被下面统一化
        #if j>=w[0]:dp[j]=v[0] #dp[j]=max(0,v[i]+0)

    for i in range(len(w)):#1->0
        for j in range(w[i],-1,-1): #转移方向,使基于前面的状态没有被提前改变
            dp[j]=max(dp[j],v[i]+dp[j-w[i]])
    return dp[c]

print(knapsack01([3,6,7],[15,14,13],10))

完全背包问题:每种背包可以重复取
【日常系列】LeetCode《27·动态规划2》

#dfs
def knapsackComplete(w, v, c):
    def dfs(start,remain_w):
        maxValue=0
        #处理子节点
        for i in range(start,len(w)):
            child_i=i
            #剪枝
            if child_i==-1 or child_i==len(w):continue
            if remain_w-w[child_i]<0:continue
            maxValue=max(maxValue,dfs(child_i,remain_w-w[child_i]))
        k=0 if start==-1 else v[start]
        return maxValue + k

    return dfs(-1, c)
print(knapsackComplete([3,6,7],[16,14,13],10))

#dp
def knapsackComplete(w, v, c):
    dp = [[0] * (c + 1) for _ in range(len(w))]

    for j in range(c + 1):
        dp[0][j] = (j//w[0])*v[0]

    for i in range(1, len(w)):
        for j in range(c + 1):
            max_cnt=j//w[i]
            for k in range(max_cnt+1):
                dp[i][j] = max(dp[i][j], k*v[i] + dp[i - 1][j - k*w[i]]) #key
    return dp[len(w) - 1][c]

print(knapsackComplete([3,3,7],[18,17,13],10))

#dp+时空优化【重】
def knapsackComplete(w, v, c):
    dp = [0] * (c + 1) #空间优化

    #for j in range(c + 1):
        #dp[j] = (j//w[0])*v[0]

    for i in range(len(w)):
        for j in range(w[i],c + 1):
        #放 第一个物品产生的价值永远大于等于放 第 2、3、4、5.... 个
        #如果放第一个物品产生的价值比不放这个物品产生的价值要小的话,那么不放物品,产生的价值最大
            dp[j]=max(dp[j],v[i]+dp[j-w[i]]) #时间优化 #key:#转移方向:使基于前面的状态被提前改变
            # max_cnt=j//w[i]
            # for k in range(max_cnt+1):
            #     dp[j] = max(dp[j], k*v[i] + dp[j - k*w[i]]) #key
    return dp[c]

print(knapsackComplete([3,3,7],[18,17,13],10))

#多重背包
def knapsackM(w, v, c, p):
    dp = [0] * (c + 1) #空间优化

    for i in range(len(w)):
        for j in range(c,w[i]-1,-1):#key:非重复选(<=p[i])
            max_cnt=min(j//w[i],p[i])
            for k in range(max_cnt+1):
                dp[j] = max(dp[j], k*v[i] + dp[j - k*w[i]]) #key
    return dp[c]

print(knapsackM([3,3,7],[18,17,13],10,[2,2,0]))


#二维背包
def knapsackComplete(w,g,W,G,v):#两种代价
    dp = [[0] * (G+1) for _ in range(W+1)] #空间优化

    for i in range(len(w)):
        for j in range(W,w[i]-1,-1):
            for k in range(G,g[i]-1,-1):
                dp[j][k] = max(dp[j][k], v[i] + dp[j-w[i]][k-g[i]]) #key

    return dp[w][G]

【日常系列】LeetCode《27·动态规划2》

lc 322【剑指 103】:零钱兑换
https://leetcode.cn/problems/coin-change/
提示:
1 <= coins.length <= 12
1 <= coins[i] <= 2^31 - 1
0 <= amount <= 10^4
【日常系列】LeetCode《27·动态规划2》

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp=[float('inf')]*(amount+1)
        
        dp[0]=0 #key-遗漏
        for i in range(len(coins)):
            for j in range(coins[i],amount+1):
                dp[j]=min(dp[j],1+dp[j-coins[i]])
        
        return dp[amount] if dp[amount]!=float('inf') else -1


lc 518 :零钱兑换 II
https://leetcode.cn/problems/coin-change-ii/
提示:
1 <= coins.length <= 300
1 <= coins[i] <= 5000
coins 中的所有值 互不相同
0 <= amount <= 5000
【日常系列】LeetCode《27·动态规划2》

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        dp=[0]*(amount+1)
        
        dp[0]=1 #key:例如dp[5]=dp[5]+dp[0],5凑5本身就是一种组合
        for i in range(len(coins)):
            for j in range(coins[i],amount+1):
                dp[j]=dp[j]+dp[j-coins[i]] #上一个状态中,本身的组合数+新coins[i]带来的新状态中,能得到的组合数
        
        return dp[amount]

lc 377【剑指 104】 :组合总和 Ⅳ
https://leetcode.cn/problems/combination-sum-iv/
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 1000
nums 中的所有元素 互不相同
1 <= target <= 1000
进阶:
如果给定的数组中含有负数会发生什么?问题会产生何种变化?如果允许负数出现,需要向题目中添加哪些限制条件?
【日常系列】LeetCode《27·动态规划2》

#请注意,顺序不同的序列被视作不同的组合。
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): #key:for顺序
            for i in range(len(nums)):
                if j>=nums[i]:
                    dp[j]=dp[j]+dp[j-nums[i]]
        return dp[target]

lc 494【剑指 102】【top100】:目标和
https://leetcode.cn/problems/target-sum/
提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000

# 假设数组中所有数字的总和为 sum
# 假设前面设置为负数的数字的总和是 neg。那么设置为正数的数字的总和为 sum - neg
# 那么 (sum - neg) - neg = target => neg = (sum - target)/ 2
# 所以问题转为 0-1 背包问题:
# 在数组 nums 列表中不可重复的选择数字组合,使得组合中所有数字之和为 neg
# 求有多少组合数?
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        diff=sum(nums)-target
        if diff%2==1 or diff<0:return 0 #key:sum(nums)>=0,target<=sum(nums)
        #
        neg=diff//2
        dp=[0]*(neg+1)
        dp[0]=1 #遗忘
        for i in range(len(nums)):
            for j in range(neg,nums[i]-1,-1):
                dp[j]=dp[j]+dp[j-nums[i]]
        return dp[neg]

lc 416 【剑指 101】【top100】:分割等和子集
https://leetcode.cn/problems/partition-equal-subset-sum/
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 100
【日常系列】LeetCode《27·动态规划2》

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        if sum(nums)%2==1:return False
        target=sum(nums)//2
        #
        dp=[False]*(target+1)
        dp[0]=True #遗忘
        for i in range(len(nums)):
            for j in range(target,nums[i]-1,-1):
                dp[j]=dp[j] or dp[j-nums[i]]
        
        return dp[target]

lc 279 :完全平方数【top100】
https://leetcode.cn/problems/perfect-squares/
提示:
1 <= n <= 10^4

# 完全平方数最小为 1,最大为 sqrt(n)
# 也就是我们要从 nums = [1, 2, ..., sqrt(n)] 数组里选出几个数,令其平方和为 target = n。
# 转化为是否可以用 nums 中的数(可重复选用)组合和成 n
class Solution:
    def numSquares(self, n: int) -> int:
        dp=[float('inf')]*(n+1)
        dp[0]=0
        for i in range(1,int(math.sqrt(n))+1):
            for j in range(i**2,n+1):
                dp[j]=min(dp[j],dp[j-i**2]+1)
        return dp[n]

lc 474 :一和零
https://leetcode.cn/problems/ones-and-zeroes/
提示:
1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i] 仅由 ‘0’ 和 ‘1’ 组成
1 <= m, n <= 100

# 二维费用背包问题
# 物品是字符串数组中的字符串,选择每个字符串有两个代价,分别是 0 的个数和 1 的个数
# 两个代价都有最大值,0 的个数最多为 m,1 的个数最多为 n
# 求选择字符串得到的最大子集的大小
class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        dp=[[0]*(n+1) for _ in range(m+1)]

        for i in range(len(strs)):
            cnt=self.cntzeroone(strs[i])
            for j in range(m,cnt[0]-1,-1): #cnt[0]->w[j],cnt[1]->v[k]
                for k in range(n,cnt[1]-1,-1):
                    dp[j][k]=max(dp[j][k],dp[j-cnt[0]][k-cnt[1]]+1)
        
        return dp[m][n]
    
    def cntzeroone(self,s):
        cnt=[0]*2
        for i in range(len(s)):
            cnt[ord(s[i])-ord('0')]+=1
        return cnt

lc 139【top100】:单词拆分
https://leetcode.cn/problems/word-break/
注意:
不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
提示:
1 <= s.length <= 300
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s 和 wordDict[i] 仅有小写英文字母组成
wordDict 中的所有字符串 互不相同文章来源地址https://www.toymoban.com/news/detail-423548.html

# 在 wordDict 中可重复的选择字符串组合,看看是否存在可以组成字符串 s
# dp[i]: 表示前 i 个字符组成的子串是否可以被 wordDict 中的字符串组合而成
# 注意:这里的组合的顺序是任意的,所以先选择字符,再选择每个字典字符串
class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        dp=[False]*(len(s)+1)
        dp[0]=True
        for i in range(1,len(s)+1):
            for word in wordDict:
                if i>=len(word) and s[i-len(word):i]==word:#key:状态转移
                    dp[i]=dp[i] or dp[i-len(word)]
        return dp[len(s)]

到了这里,关于【日常系列】LeetCode《27·动态规划2》的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【手撕算法|动态规划系列No.2】leetcode面试题 08.01. 三步问题

    个人主页:平行线也会相交 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望对大家有所帮助 🍓希望我们一起努力、成长,共同进步。

    2024年02月12日
    浏览(64)
  • 【手撕算法|动态规划系列No.1】leetcode1137. 第 N 个泰波那契数

    个人主页:平行线也会相交 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望对大家有所帮助 🍓希望我们一起努力、成长,共同进步。

    2024年02月11日
    浏览(56)
  • 【LeetCode动态规划#11】打家劫舍系列题(涉及环结构和树形DP的讨论)

    力扣题目链接(opens new window) 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非

    2023年04月21日
    浏览(38)
  • 【LeetCode股票买卖系列:309. 最佳买卖股票时机含冷冻期 | 暴力递归=>记忆化搜索=>动态规划】

    🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文

    2024年02月02日
    浏览(46)
  • 【LeetCode动态规划#14】子序列系列题(最长递增子序列、最长连续递增序列、最长重复子数组、最长公共子序列)

    力扣题目链接(opens new window) 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 示例 1: 输入:nums = [10,9,2,5,3,7,101,18] 输出

    2024年02月01日
    浏览(56)
  • 【学会动态规划】摆动序列(27)

    目录 动态规划怎么学? 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后: 学习一个算法没有捷径,更何况是学习动态规划, 跟我一起刷动态规划算法题,一起学会动态规划! 题目链接:376. 摆动序列 - 力扣(LeetCo

    2024年02月11日
    浏览(37)
  • 算法27:从暴力递归到动态规划(2)

    上一题比较简单,下面来一道比较难的题目。 假设有排成一行的 N 个位置,记为 1~ N , N 一定大于或等于 2 开始时机器人在其中的 M 位置上 ( M 一定是 1~ N 中的一个 ) 如果机器人来到 1 位置,那么下一步只能往右来到 2 位置; 如果机器人来到 N 位置,那么下一步只能往左来到

    2024年02月09日
    浏览(38)
  • LeetCode练习七:动态规划上:线性动态规划

    参考《OI Wiki动态规划》、《算法通关手册》动态规划篇 1.1 动态规划简介    动态规划(Dynamic Programming) :简称 DP ,是一种通过把原问题分解为相对简单的子问题的方式而求解复杂问题的方法。 动态规划方法与分治算法类似,却又不同于分治算法。 「动态规划的核心思想

    2024年02月12日
    浏览(70)
  • LeetCode——动态规划篇(二)

     刷题顺序及思路来源于代码随想录,网站地址:https://programmercarl.com  目录 343. 整数拆分 - 力扣(LeetCode) 96. 不同的二叉搜索树 - 力扣(LeetCode) 416. 分割等和子集 - 力扣(LeetCode) 1049. 最后一块石头的重量 II - 力扣(LeetCode)   给定一个正整数  n  ,将其拆分为  k  个 

    2024年02月07日
    浏览(35)
  • LeetCode —— 动态规划

    持续更新中..................................... 509. 斐波那契数 斐波那契数 (通常用  F(n)  表示)形成的序列称为 斐波那契数列 。该数列由  0  和  1  开始,后面的每一项数字都是前面两项数字的和。也就是:F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n 1。 示例: 输入: n

    2024年02月09日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包