Leetcode刷题第十一天-动态规划

🤯🤯🤯跟着卡哥开启动态规划之旅  代码随想录 (programmercarl.com)  🤯🤯🤯

动态规划五步:

1、确定dp数组(dp table)以及下标的含义
2、确定递推公式
3、dp数组如何初始化
4、确定遍历顺序
5、举例推导dp数组

509:斐波那契数

链接:509. 斐波那契数 - 力扣(LeetCode)

 1 class Solution:
 2     def fib(self, n: int) -> int:
 3         #dp[i]第i个斐波那契数的数值为dp[i]
 4         #推到公式dp[i]=dp[i-1]+dp[i-1]
 5         #dp数组初始化dp[0]=0,dp[1]=1
 6         #遍历顺序 从前向后
 7         #打印dp数组
 8         if(n<2):    return n
 9         dp=[0,1]
10         for i in range(2,n+1):
11             dp.append(dp[i-1]+dp[i-2])
12         return dp[n]
fib

70:爬楼梯

链接:70. 爬楼梯 - 力扣(LeetCode)

 1 class Solution:
 2     def climbStairs(self, n: int) -> int:
 3         #dp[i]第i个台阶有dp[i]方法
 4         #推到公式dp[i]=dp[i-1]+dp[i-2]
 5         #dp数组初始化dp[0]=1,dp[1]=2
 6         #遍历顺序 从前向后
 7         #打印dp数组
 8         dp=[1,2]
 9         if(n<3):    return dp[n-1]
10         for i in range(2,n):
11             dp.append(dp[i-1]+dp[i-2])
12         return dp[n-1]
13         
climbStairs

746:使用最小花费爬楼梯

链接:746. 使用最小花费爬楼梯 - 力扣(LeetCode)

 1 class Solution:
 2     def minCostClimbingStairs(self, cost: List[int]) -> int:
 3         #dp[i]到i个台阶最小消耗dp[i]
 4         #推到公式dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
 5         #dp数组初始化dp[0]=0,dp[1]=0
 6         #遍历顺序 从前向后
 7         #打印dp数组
 8         dp=[0,0]
 9         for i in range(2,len(cost)+1):
10             dp.append(min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]))
11             print(dp[i])
12         return dp[len(cost)]
minCostClimbingStairs

62:不同路径

链接:62. 不同路径 - 力扣(LeetCode)

从0开始遍历时,dp数组中值为1的位置需要跳过,只能向下或向右,第一行和第一列的每个格子均只有1种情况,跳过

也可以从1开始遍历

 1 class Solution:
 2     def uniquePaths(self, m: int, n: int) -> int:
 3         #dp[i][j]到ijw位置有dp[i][j]方法
 4         #推到公式dp[i][j]=dp[i-1][j]+dp[i][j-1]
 5         #dp数组初始化dp[i][0]=1,dp[0][j]=1
 6         #遍历顺序 从前向后
 7         #打印dp数组
 8         dp=[[0 for i in range(n)] for j in range(m)]
 9         for i in range(m):  dp[i][0]=1
10         for i in range(n):  dp[0][i]=1
11         for i in range(m):
12             for j in range(n):
13                 if not dp[i][j]:
14                     dp[i][j]=dp[i-1][j]+dp[i][j-1]
15         return dp[m-1][n-1]
uniquePaths
for i in range(1,m):
    for j in range(1,n):
        dp[i][j]=dp[i-1][j]+dp[i][j-1]

63:不同路径II

链接:63. 不同路径 II - 力扣(LeetCode)

与62的区别:初始化时如果第一行第一列有障碍,那么障碍后面的格子值为0

 1 class Solution:
 2     def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
 3         #dp[i][j]到ijw位置有dp[i][j]方法
 4         #推到公式dp[i][j]=dp[i-1][j]+dp[i][j-1]
 5         #dp数组初始化dp[i][0]=1,dp[0][j]=1,如果遇到当前位置遇到obstacleGrid[i][0]=1,obstacleGrid[0][i]=1,dp下面保持0
 6         #遍历顺序 从前向后
 7         #打印dp数组
 8         m,n=len(obstacleGrid),len(obstacleGrid[0])
 9         dp=[[0 for i in range(n)] for i in range(m)]
10         for i in range(m):
11             if not obstacleGrid[i][0]:  dp[i][0]=1
12             else:   break
13         for i in range(n):
14             if not obstacleGrid[0][i]:  dp[0][i]=1
15             else:   break
16         for i in range(1,m):
17             for j in range(1,n):
18                 if(obstacleGrid[i][j]): dp[i][j]=0
19                 else:   dp[i][j]=dp[i-1][j]+dp[i][j-1]
20         return dp[m-1][n-1]
uniquePathsWithObstacles

343:整数拆分

链接:343. 整数拆分 - 力扣(LeetCode)

推导公式dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]) for j in range(i)),拆俩数:j,i-j;拆3个数以上j,dp[i-j](至少是拆俩数的情况)

 1 class Solution:
 2     def integerBreak(self, n: int) -> int:
 3         #dp[i]拆分成i个数的最大乘积
 4         #推到公式dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]) for j in range(i))
 5         #拆俩数:j,i-j;拆3个数以上j,dp[i-j](至少是拆俩数的情况)
 6         #dp数组初始化dp[2]=1,dp[1]=0,dp=[0]=0
 7         #遍历顺序 从前向后,从3开始
 8         #打印dp数组
 9         dp=[0,0,1]
10         for i in range(3,n+1):
11             num=0
12             #遍历到i的一半即可
13             for j in range(1,i//2 +1):
14                 num=max(num,max(j*(i-j),j*dp[i-j]))
15             dp.append(num)
16         return dp[n]
17         '''
18         #贪心
19         sums=1
20         middle=n//2 +1
21         for i in range(2,n):
22             num=n//i
23             sums=max(sums,(num**(i-n%i))*((num+1)**(n%i)))
24         return sums
25         '''
26         
integerBreak

96:不同二叉搜索数

链接:96. 不同的二叉搜索树 - 力扣(LeetCode)

 1 class Solution:
 2     def numTrees(self, n: int) -> int:
 3         #dp[i]由i个数组成的二叉搜索数情况
 4         #推导公式dp[i]=sum(dp[i-j]*dp[j-1]) j为头节点
 5         #dp数组初始化dp[0]=1,dp[1]=1,dp=[2]=2
 6         #遍历顺序 从前向后,从3开始
 7         #打印dp数组
 8         dp=[1,1,2]
 9         for i in range(n-2):    dp.append(0)
10         for i in range(3,n+1):
11             for j in range(1,i+1):
12                 dp[i]+=dp[i-j]*dp[j-1]
13         return dp[n]
numTrees

🤯🤯🤯🤯背个背包吧🤯🤯🤯🤯(●'◡'●)(●'◡'●)从卡哥那偷个图φ(゜▽゜*)♪φ(゜▽゜*)♪

背包分类:

0-1背包:n种物品,每个物品只有一个
完全背包:n种物品,每个物品有无限个
多重背包:n种物品,每个物品个数不同

0-1背包:

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。

小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。

第一行包含两个正整数,第一个整数 M 代表研究材料的种类,第二个正整数 N,代表小明的行李空间。

第二行包含 M 个正整数,代表每种研究材料的所占空间。

第三行包含 M 个正整数,代表每种研究材料的价值。

#dp[i][j]空间大小为i得背包可以放入材料得最大价值
#推导公式dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])
#dp数组初始化dp[0][j]=0,dp[i][0]=v[0]
#遍历顺序
#打印dp数组
def bag(m,n,w,v):
    dp=[[0 for i in range(n+1)] for i in range(m)]
    for i in range(n+1):
        if(i>=w[0]):    dp[0][i]=v[0]
    #遍历物品
    for i in range(1,m):
        #遍历背包
        for j in range(n+1):
            if(j<w[i]): dp[i][j]=dp[i-1][j]
            else:   dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])
    print(dp[m-1][n])
    #return dp[m-1][n]
m,n=map(int,input().strip().split(' '))
w=list(map(int,input().strip().split()))
v=list(map(int,input().strip().split()))
bag(m,n,w,v)
View Code
 1 #dp降维度一维数组,第一个物品遍历完,将所有背包容量覆盖到第二个物品中
 2 #推导公式dp[i][j]=max(dp[j], dp[j - w[i]] + v[i])
 3 def bag1(m,n,w,v):
 4     #容量为j的背包可以放的最大价值,初始化为0
 5     dp=[0 for j in range(n+1)]
 6     #遍历物品
 7     for i in range(m):
 8         #遍历背包,从后往前遍历,一个物品只放一次
 9         for j in range(n,w[i]-1,-1):
10             dp[j] = max(dp[j], dp[j - w[i]] + v[i])
11     print(dp[n])
滚动数组

416:分割等和子集

链接:416. 分割等和子集 - 力扣(LeetCode)

 1 class Solution:
 2     def canPartition(self, nums: List[int]) -> bool:
 3         #dp[i]容量为i的背包所能放的最大价值,背包最大容量为sum(nums)//2
 4         #推导公式dp[i]=max(dp[i], dp[i-num]+num),nums数据组第i个元素即表示物品重量又表示物品价值,w,v
 5         #dp数组初始化dp=[0]*n
 6         #遍历顺序:背包倒序
 7         #打印dp数组
 8         sums=sum(nums)
 9         if(sums%2==1):  return False
10         n=sums//2
11         dp=[0]*(n+1)
12         #遍历物品
13         for num in nums:
14             #遍历背包
15             for i in range(n,num-1,-1):
16                 dp[i]=max(dp[i], dp[i-num]+num)
17         if(dp[n]==n):   return True
18         return False
19         
canPartition

 

posted @ 2024-02-20 17:40  小小小茹茹  阅读(18)  评论(0编辑  收藏  举报