1 线性动态规划

1.1 单串

T1 最长递增子序列

class Solution(object):def lengthOfLIS(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)dp=[0]*lfor i in range(l):if i==0:dp[i]=1else:for x in range(i):temp=0if nums[x]<nums[i]:temp=dp[x]+1#dp[i]=max(dp[i],dp[x]+1)else:temp=1#dp[i]=max(dp[i],1)if temp>dp[i]:dp[i]=tempreturn max(dp)

T2 最长递增子序列的个数

#错误尝试
class Solution(object):def findNumberOfLIS(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)dp=[0]*lcnt=0 #这个也是要符合dp的呀,没法就用一个值处理掉的for i in range(l):if i==0:dp[i]=1else:for x in range(i):temp=0if nums[x]<nums[i]:temp=dp[x]+1else:temp=1if temp>dp[i]:dp[i]=tempm=max(dp)for i in range(l):if dp[i]==m:for x in range(i):if dp[x]==m-1:cnt+=1if cnt==0:cnt=lreturn cnt
#正确做法(保守)
class Solution(object):def findNumberOfLIS(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)dp=[0]*lcnt=[1]*lfor i in range(l):if i==0:dp[i]=1else:for x in range(i):if nums[x]<nums[i]:temp=dp[x]+1else:temp=1if temp>dp[i]:dp[i]=temp#这里开启了第二次循环,实际是为了防止dp[i]没有处理到最终,没敢加到过程里for x in range(i):if nums[x]<nums[i] and dp[x]==dp[i]-1:cnt[i]+=cnt[x]if cnt[i]!=1:cnt[i]-=1m=max(dp)c=0for i in range(l):if dp[i]==m:c+=cnt[i]return c
#正确答案(稍微简化版)
class Solution(object):def findNumberOfLIS(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)dp=[0]*lcnt=[1]*lfor i in range(l):if i==0:dp[i]=1else:for x in range(i):if nums[x]<nums[i]:temp=dp[x]+1if temp>dp[i]:dp[i]=tempcnt[i]=cnt[x] #1#受到答案启发,只要dp[i]还在变,cnt[i]也就直接受到影响变化,因此最终停留的初始状态一定是dp[i]成熟了对应的情况,因而不存在上面的那个担忧elif temp==dp[i]:cnt[i]+=cnt[x] #2else:temp=1if temp>dp[i]:dp[i]=tempm=max(dp)c=0for i in range(l):if dp[i]==m:c+=cnt[i]return c
#再简化一点,即答案的写法
class Solution(object):def findNumberOfLIS(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)dp=[1]*l #区别在这里其实就是少了对于i==0的那些操作了cnt=[1]*l if l<=1:return lfor i in range(1,l):for x in range(i):if nums[x]<nums[i]:temp=dp[x]+1if temp>dp[i]:dp[i]=tempcnt[i]=cnt[x] #1elif temp==dp[i]:cnt[i]+=cnt[x] #2m=max(dp)c=0for i in range(l):if dp[i]==m:c+=cnt[i]return c

T3 最大子数组和

class Solution(object):def maxSubArray(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)if l==0:return 0dp=[0]*lfor i in range(l):if i==0:dp[i]=nums[i]else:#dp[i]=max(dp[i-1]+nums[i],nums[i])if dp[i-1]>0:dp[i]=dp[i-1]+nums[i] else:dp[i]=nums[i]return max(dp)

T4 乘积最大子数组

​ 我想到了不符合最优子结构,想到了要区分正负进行不同操作,但是没有想到可以通过储存两个,一个max一个min来处理并且是正确的。其实问题就在于,动归不考虑后面的未知,而是把子问题截断在当下,把每一个当下当作结尾,我想不出来是因为我考虑到,假设此刻是个正数,如果后面还存在负数,那我此刻完全可以选择前面那个负的大的(当然这样还要去比较和全正哪个大),但是如果后面只存在正数,就必须选正的,这样一来就会非常的复杂,摸不着头脑。但是我之所以会这样是因为,我发现了它不符合最优子结构就不知道怎么办了,虽然我知道还是要动归,但是不知道该改哪里,所以就不小心跳出了动归的本质。但动归本质是一定要截断在当下的,不会去考虑当下以后的未知。所以如果截断在当下,以当下为末尾,那么如果当下为负,就去乘上一个的最小得到最大,如果当下为正,就去乘上一个的最大得到最大,为了符合结构一致性,也要用类似的方法维护每个当下得到的最,这样就能得到正规的思路了。

class Solution(object):def maxProduct(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)maxdp=[0]*lmindp=[0]*l if l==0:returnfor i in range(l):if i==0:maxdp[i]=nums[i]mindp[i]=nums[i]else:if nums[i]<=0:if mindp[i-1]<=0:maxdp[i]=mindp[i-1]*nums[i]else:maxdp[i]=nums[i]if maxdp[i-1]<=0:mindp[i]=nums[i]else:mindp[i]=maxdp[i-1]*nums[i]else: #nums[i]>0if maxdp[i-1]<=0:maxdp[i]=nums[i]else:maxdp[i]=maxdp[i-1]*nums[i]if mindp[i-1]<=0:mindp[i]=mindp[i-1]*nums[i]else:mindp[i]=nums[i]return max(maxdp)

T5 环形子数组的最大和

【法一】暴力×动归

​ 选择的方法是,对于外圈进行暴力迭代,对于针对的每一个值,取出其对应的数组并进行动归。逻辑是正确的,但是对于最大的测试点会超时。

#逻辑正确,但效率有点低,对于最长的测试点会超时
class Solution(object):def helper(self,nums):l=len(nums)dp=[0]*l for i in range(l):if i==0:dp[i]=nums[i]else:if dp[i-1]>0:dp[i]=dp[i-1]+nums[i]else:dp[i]=nums[i]return dp[-1]def maxSubarraySumCircular(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)final=[0]*l for i in range(l):a=[]for j in range(1,l+1):a.append(nums[(i+j)%l])final[i]=self.helper(a)return max(final)

失败的简化尝试:

(试图变成动归×动归,但是事实上本题的外圈不能随随便便动归)

​ 试图通过这种方式进行一个简化,但是并不对,因为它和普通最大子序和的区别不仅仅是初始的求,还在于每一次都要限制长度,如果只用helper算出初始值,然后就递推下去,是不对的,因为这样实际子串长度就超过l了,所以实际上,这个从大问题上来看也是不符合最优子结构的。

#错误的
class Solution(object):def helper(self,nums):l=len(nums)dp=[0]*l for i in range(l):if i==0:dp[i]=nums[i]else:if dp[i-1]>0:dp[i]=dp[i-1]+nums[i]else:dp[i]=nums[i]return dp[-1]def maxSubarraySumCircular(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)final=[0]*l for i in range(l):if i==0:a=[]for j in range(1,l+1):a.append(nums[(i+j)%l])final[i]=self.helper(a)else:if final[i-1]>0:final[i]=final[i-1]+nums[i]else:final[i]=nums[i]return max(final)
【法二】分情况/运用反面/min.max

​ 思路是这样的,因为环形嘛,所以就分为两种情况,一种是最大子数组恰好是连着的那部分;另一种是最大子数组是断开分为两半的,而其对立面(即不取的那部分)恰好是连着的,就可以考虑使用反面,用整体减去连续的最小子数组。正确答案就是这两者其中最大的那个。不过有一个非常关键易被忽略的边界问题就是,用总体去减min的时候存在全都剪掉(就是因为所有的元素都是负的,那肯定是一个都不取得到的值最大)的可能,也就是取出来的是空数组,但题目要求了是非空数组,所以必须避免这种情况,所以在全为负的情况下,返回最大值就可。

class Solution(object):def maxSubarraySumCircular(self, nums):l=len(nums)if l==0:returnif l==1:return nums[0]maxdp=[0]*l mindp=[0]*l s=0for i in range(l):s+=nums[i]if i==0:maxdp[i]=nums[0]mindp[i]=nums[0]else:if maxdp[i-1]>0:maxdp[i]=maxdp[i-1]+nums[i]else:maxdp[i]=nums[i]if mindp[i-1]<0:mindp[i]=mindp[i-1]+nums[i]else:mindp[i]=nums[i]a=max(maxdp)b=s-min(mindp)#判断是否全部非负if a<0:return areturn max(a,b)

T6 最大子矩阵

​ 首先进行了错误的尝试,认为它可以动归乘以动归,也就是两个独立的符合动归的问题,但这个主要问题是,不能对齐,每一行取到的可能并不对齐,违背了矩阵的要求。然后我想到了,要对齐,就可以使用加和的那种,还是两个动归,但是仔细想了一下,加和的话,外面那层就不符合最优子结构了。所以综上,照这个思路还是只能递归×动归(也就是首先列举n²也就是所有可能的加和,变成N²×M矩阵,然后再逐行动归,后半部分也就是和第一次的尝试基本完全相同的),但是个人感觉这个一定会超时,而且好像用前缀和解决会更巧妙一点?还没学,暂且搁置……

#第一次的,错误的尝试
class Solution(object):def helper(self,nums):l=len(nums)if l==0:returndp=[0]*l x1=[0]*lfor i in range(l):if i==0:dp[i]=nums[0]else:if dp[i-1]>0:dp[i]=dp[i-1]+nums[i]x1[i]=x1[i-1]else:dp[i]=nums[i]x1[i]=i mx2=dp.index(max(dp))mx1=x1[mx2]return max(dp),mx1,mx2def getMaxMatrix(self, matrix):""":type matrix: List[List[int]]:rtype: List[int]"""l1=len(matrix)if l1==0:return []l2=len(matrix[0])dp=[0]*l1y1=[0]*l1for i in range(l1):if i==0:dp[i]=self.helper(matrix[0])[0]else:temp=self.helper(matrix[i])[0]if dp[i-1]>0:dp[i]=dp[i-1]+tempy1[i]=y1[i-1]else:dp[i]=tempy1[i]=imy2=dp.index(max(dp))my1=y1[my2]mx1_=self.helper(matrix[my1])[1]mx2_=self.helper(matrix[my1])[2]mx1__=self.helper(matrix[my2])[1]mx2__=self.helper(matrix[my2])[2]     mx1=min(mx1_,mx1__)  mx2=max(mx2_,mx2__)  return [my1,mx1,my2,mx2]

T7 打家劫舍Ⅰ

class Solution(object):def rob(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)if l==0:return 0if l==1:return nums[0]if l==2:return max(nums)dp=[0]*l for i in range(l):if i==0:dp[i]=nums[0]elif i==1:dp[i]=max(dp[i-1],nums[i])else:dp[i]=max(dp[i-1],nums[i]+dp[i-2])return max(dp)

T8 打家劫舍Ⅱ

class Solution(object):def rob(self, nums):""":type nums: List[int]:rtype: int"""l=len(nums)if l==0:return 0if l==1:return nums[0]if l==2:return max(nums)dp1=[0]*(l-1)dp2=[0]*(l-1)for i in range(l-1):if i==0:dp1[0]=nums[0]dp2[0]=nums[1]else:dp1[i]=max(dp1[i-1],dp1[i-2]+nums[i])dp2[i]=max(dp2[i-1],dp2[i-2]+nums[i+1])return max(max(dp1),max(dp2))

1.2 双串

T1 最长公共子序列

class Solution(object):def longestCommonSubsequence(self, text1, text2):""":type text1: str:type text2: str:rtype: int"""l1=len(text1)l2=len(text2)dp=[[0]*(l2+1) for i in range(l1+1)]for i in range(1,l1+1):for j in range(1,l2+1):if text1[i-1]==text2[j-1]:dp[i][j]=dp[i-1][j-1]+1else:dp[i][j]=max(dp[i-1][j],dp[i][j-1])return dp[l1][l2]

T2 编辑距离

# 错了
class Solution(object):def minDistance(self, word1, word2):""":type word1: str:type word2: str:rtype: int"""l1=len(word1)l2=len(word2)dp=[[0]*(l2+1) for a in range(l1+1)]dic={}for i in range(1,l1+1):for j in range(1,l2+1):if word1[i-1]==word2[j-1]:dp[i][j]=dp[i-1][j-1]+1temp=dp[i][j]if temp not in dic:l=i-jif l>=0:dic[temp]=lelse:dic[temp]=-l else:dp[i][j]=max(dp[i-1][j],dp[i][j-1])ops=0for x in dic.keys():ops+=dic[x]return dic
#对了
class Solution(object):def minDistance(self, word1, word2):""":type word1: str:type word2: str:rtype: int"""l1=len(word1)l2=len(word2)dp=[[0]*(l2+1) for a in range(l1+1)]for i in range(l1+1):dp[i][0]=ifor j in range(l2+1):dp[0][j]=jfor i in range(1,l1+1):for j in range(1,l2+1):if word1[i-1]==word2[j-1]:dp[i][j]=dp[i-1][j-1]else:dp[i][j]=min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1return dp[l1][l2]

T3 最小路径和

#一遍√
class Solution(object):def minPathSum(self, grid):""":type grid: List[List[int]]:rtype: int"""l1=len(grid)l2=len(grid[0])dp=[[0]*(l2+1) for i in range(l1+1)]for i in range(l1+1):dp[i][0]=float('inf')for j in range(l2+1):dp[0][j]=float('inf')for i in range(1,l1+1):for j in range(1,l2+1):if i==1 and j==1:dp[i][j]=grid[i-1][j-1]else:dp[i][j]=grid[i-1][j-1]+min(dp[i-1][j],dp[i][j-1])return dp[l1][l2]

2 前缀和

T1 区域和检索 - 数组不可变

#1 暴力迭代×动归(超时)
class NumArray(object):def __init__(self, nums):""":type nums: List[int]"""self.l=len(nums)self.n=numsdef sumRange(self, left, right):""":type left: int:type right: int:rtype: int"""dp=[[0]*self.l for x in range(self.l)]for i in range(self.l):for j in range(i,self.l):if i==j:dp[i][j]=self.n[j]else:dp[i][j]=self.n[j]+dp[i][j-1]return dp[left][right]
#2 前缀和动归
class NumArray(object):def __init__(self, nums):""":type nums: List[int]"""self.l=len(nums)self.n=numsdef sumRange(self, left, right):""":type left: int:type right: int:rtype: int"""dp=[0]*self.lfor i in range(self.l):if i==0:dp[i]=self.n[i]else:dp[i]=dp[i-1]+self.n[i]if left>=1:ret=dp[right]-dp[left-1]else:ret=dp[right]return ret

T2 二维区域和检索 - 矩阵不可变

#1 纯暴力【超时】
class NumMatrix(object):def __init__(self, matrix):""":type matrix: List[List[int]]"""self.m=matrixdef sumRegion(self, row1, col1, row2, col2):""":type row1: int:type col1: int:type row2: int:type col2: int:rtype: int"""s=0for i in range(row1,row2+1):s+=sum(self.m[i][col1:col2+1])return s # Your NumMatrix object will be instantiated and called as such:
# obj = NumMatrix(matrix)
# param_1 = obj.sumRegion(row1,col1,row2,col2)
返回该题
#2 一维前缀和×暴力【超时】
class NumMatrix(object):def __init__(self, matrix):""":type matrix: List[List[int]]"""self.m=matrixdef sumRegion(self, row1, col1, row2, col2):""":type row1: int:type col1: int:type row2: int:type col2: int:rtype: int"""l1=len(self.m)l2=len(self.m[0])dp=[[0]*l2 for i in range(l1)]for i in range(l1):for j in range(l2):if j==0:dp[i][j]=self.m[i][j]else:dp[i][j]=dp[i][j-1]+self.m[i][j]s=0for x in range(row1,row2+1):if col1>=1:temp=dp[x][col2]-dp[x][col1-1]else:temp=dp[x][col2]s+=tempreturn s # Your NumMatrix object will be instantiated and called as such:
# obj = NumMatrix(matrix)
# param_1 = obj.sumRegion(row1,col1,row2,col2)
#二维前缀和×暴力(还是超时了。。我不理解)
class NumMatrix(object):def __init__(self, matrix):""":type matrix: List[List[int]]"""self.m=matrixdef sumRegion(self, row1, col1, row2, col2):""":type row1: int:type col1: int:type row2: int:type col2: int:rtype: int"""l1=len(self.m)l2=len(self.m[0])dp=[[0]*(l2+1) for x in range(l1+1)]for i in range(1,l1+1):for j in range(1,l2+1):if i==1 and j==1:dp[i][j]=self.m[i-1][j-1]else:dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+self.m[i-1][j-1]return dp[row2+1][col2+1]-dp[row2+1][col1]-dp[row1][col2+1]+dp[row1][col1]# Your NumMatrix object will be instantiated and called as such:
# obj = NumMatrix(matrix)
# param_1 = obj.sumRegion(row1,col1,row2,col2)

3 区间动态规划

#错了(循环选取不对)
class Solution(object):def longestPalindrome(self, s):""":type s: str:rtype: str"""l=len(s)if l==0:returnif l==1:return s if l==2:if s[0]==s[1]:return s else:return dp=[[False]*l for i in range(l)]for i in range(l):for j in range(i,l):if s[i]!=s[j]:dp[i][j]=Falseelse:if j-i<=2:dp[i][j]=Trueelse:dp[i][j]=dp[i+1][j-1]
#未完,但是这里已经犯了一个很明显的错误,就是在i的时候是不知道i+1的,但是这个遍历顺序又是,i只会经历一次,因此根本就是无效的,所以不仅仅状态转移方程重要,遍历顺序也很重要,这就体现出答案那种遍历的优越之处了,最外层遍历是区间长度,内层循环才是i,这样就符合了他那个状态推导的顺序,它一定是从比较短的推到比较长的,所以最外层循环选择从小到大的长度才是正确的。
#正确
class Solution(object):def longestPalindrome(self, s):""":type s: str:rtype: str"""#前面这些是为了处理一些总长度l比较短的情况l=len(s)if l==0:returnif l==1:return s if l==2:if s[0]==s[1]:return s else:return s[0]dp=[[False]*l for i in range(l)]maxlen=1for thelen in range(1,l+1):#这里一定记得是从1开始,前面那些特殊情况并没有包含它,因为这是可以在巨长的字符串里获得一个仅自己的回文串for i in range(l):j=i+thelen-1if j>l-1:#这里千万别不小心错写成>=,还是可以到达最长的breakif s[i]!=s[j]:dp[i][j]=Falseelse:if j-i<=2:dp[i][j]=Trueelse:dp[i][j]=dp[i+1][j-1]if dp[i][j] and j-i+1>=maxlen:maxlen=j-i+1begin=ireturn s[begin:begin+maxlen]

后续有时间会继续做区间动归和背包动归相关题目。

【leetcode记录03】动态规划相关推荐

  1. LeetCode部分习题解答记录-动态规划

    LeetCode部分习题解答记录-动态规划 动态规划 120.三角形最小路径和 方法:递归+记忆化 方法2:DP 64.最小路径和 方法1:递归 方法2:DP 从终点到起点,逐步更新 从起点到终点,逐 ...

  2. LeetCode记录总结

    LeetCode记录总结 本文章主要记录LeetCode刷题学到的知识 242.Valid Anagram 题目: Given two strings s and t , write a functi ...

  3. 【leetcode】【动态规划】股票买卖

    leetcode 股票买卖(动态规划) 这位大佬四种题型总结的很好:link 一共只有三种状态:买.卖.冷冻 buy[i]buy[i]buy[i] 表示第i天之前最后一次行为是buy时,最大的收益 s ...

  4. C#LeetCode刷题-动态规划

    动态规划篇 # 题名 刷题 通过率 难度 5 最长回文子串 22.4% 中等 10 正则表达式匹配 18.8% 困难 32 最长有效括号 23.3% 困难 44 通配符匹配 17.7% 困难 53 最 ...

  5. leetcode记录汇总

    时隔几个月,发现之前做过的题很多都忘的一干二净,所以决定记录一下经典题目的做题思路,方便复习和复盘- . . . 开始日期:2021.Feb.24 Date Title 2.24 最长递增子序列(中等 ...

  6. Leetcode学习之动态规划

    动态规划学习内容 1. 动态规划理论基础 什么是动态规划 动态规划的解题步骤 动态规划应该如何debug 2. 斐波那契数 思路 3. 爬楼梯 思路 4. 使用最小关系爬楼梯 思路 5. 不同路径 思 ...

  7. LeetCode Counting Bits(动态规划)

    问题:给出数字n,求0-n这些数的1的位数. 思路:方法一使用x&(x-1)统计数字1的位数. 方法二[0,1) [2,3)表示形式为[10,11) [4,8)表示形式为[100,101, 1 ...

  8. 【leetcode记录01】 数组

    T1. 存在重复元素 (一)集合法 class Solution:def containsDuplicate(self, nums: List[int]) -> bool:the_set=set ...

  9. LeetCode LCS 03. 主题空间(广度优先搜索BFS)

    文章目录 1. 题目 2. 解题 1. 题目 「以扣会友」线下活动所在场地由若干主题空间与走廊组成,场地的地图记作由一维字符串型数组 grid,字符串中仅包含 "0"-" ...

最新文章

  1. JAVA培训哪里好?学习Java难不难
  2. 用了这么多年的 Java 泛型,你对它到底有多了解?|原创
  3. spring websocket源码分析续Handler的使用
  4. 使用C#开发数据库应用系统
  5. html5 下拉刷新(pc+移动网页源码)
  6. hdu5693 D gamehdu 5712 D++ game
  7. spring核心包功能解析
  8. BCNF/3NF的判断方法
  9. win11如何开启GUEST账户 windows11开启GUEST账户的设置方法
  10. 有向图生成树个数(bzoj 4894: 天赋)
  11. Qt学习笔记——打开并显示图片
  12. 华硕飞行堡垒atk驱动在哪_11月8日华硕再撒大额福利 满减优惠价机不可失_第1页...
  13. 玩转力扣 - LeetCode进度倒计数表
  14. 博文翻译:Tackling the Cold Start Problem in Recommender Systems
  15. “wait_for“: 不是 “winrt::impl“ 的成员
  16. 计算机常用快捷键(世上最全)
  17. 100+个数据分析常用指标和术语
  18. Android 开发、测试工具资源汇总
  19. 【038】基于51单片机的土壤湿度自动浇花系统Proteus仿真设计
  20. 匮乏即是富足,自律产生喜悦_当惊喜与喜悦分开时

热门文章

  1. statusbar 尺寸 显示图标_StatusBar 图标展示流程 - 状态栏(StatusBar)镜头布局分析...- git完代码后的异常_169IT.COM...
  2. 解决Eclipse中SVN版本比较中文乱码问题
  3. Activity跳转的数据交换问题---Activity学习笔记(二)
  4. MapReduce简介
  5. busybox rootfs 启动脚本分析(一)
  6. Geek爱旅行 - 穿越时间的旅行
  7. asp.net环境下的静态类以及静态变量
  8. 移动Exchange2007 CCR邮箱存储路径
  9. OLEDB Excel 与C# 的数据流通方法
  10. 自动校验控件演示[含源码]