【leetcode记录03】动态规划

1 线性动态规划

1.1 单串

T1 最长递增子序列在这里插入图片描述
class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        l=len(nums)
        dp=[0]*l
        for i in range(l):
            if i==0:
                dp[i]=1
            else:
                for x in range(i):
                    temp=0
                    if 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]=temp
        return max(dp)
T2 最长递增子序列的个数

在这里插入图片描述

#错误尝试
class Solution(object):
    def findNumberOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        l=len(nums)
        dp=[0]*l
        cnt=0 #这个也是要符合dp的呀,没法就用一个值处理掉的
        for i in range(l):
            if i==0:
                dp[i]=1
            else:
                for x in range(i):
                    temp=0
                    if nums[x]<nums[i]:
                        temp=dp[x]+1
                    else:
                        temp=1
                    if temp>dp[i]:
                        dp[i]=temp
        m=max(dp)
        for i in range(l):
            if dp[i]==m:
                for x in range(i):
                    if dp[x]==m-1:
                        cnt+=1
        if cnt==0:
            cnt=l
        return cnt      
#正确做法(保守)
class Solution(object):
    def findNumberOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        l=len(nums)
        dp=[0]*l
        cnt=[1]*l
        for i in range(l):
            if i==0:
                dp[i]=1
            else:
                for x in range(i):
                    if nums[x]<nums[i]:
                        temp=dp[x]+1
                    else:
                        temp=1
                    if 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]-=1

        m=max(dp)
        c=0
        for 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]*l
        cnt=[1]*l
        for i in range(l):
            if i==0:
                dp[i]=1
            else:
                for x in range(i):
                    if nums[x]<nums[i]:
                        temp=dp[x]+1
                        if temp>dp[i]:
                            dp[i]=temp
                            cnt[i]=cnt[x] #1
                            #受到答案启发,只要dp[i]还在变,cnt[i]也就直接受到影响变化,因此最终停留的初始状态一定是dp[i]成熟了对应的情况,因而不存在上面的那个担忧
                        elif temp==dp[i]:
                            cnt[i]+=cnt[x] #2
                    else:
                        temp=1
                        if temp>dp[i]:
                            dp[i]=temp
        m=max(dp)
        c=0
        for 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 l
        for i in range(1,l):
            for x in range(i):
                if nums[x]<nums[i]:
                    temp=dp[x]+1
                    if temp>dp[i]:
                        dp[i]=temp
                        cnt[i]=cnt[x] #1
                    elif temp==dp[i]:
                        cnt[i]+=cnt[x] #2

        m=max(dp)
        c=0
        for 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 0
        dp=[0]*l
        for 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]*l
        mindp=[0]*l 
        if l==0:
            return
        for 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]>0
                    if 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:
            return
        if l==1:
            return nums[0]
        maxdp=[0]*l 
        mindp=[0]*l 
        s=0
        for 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 a
        return max(a,b)
T6 最大子矩阵

在这里插入图片描述

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

#第一次的,错误的尝试
class Solution(object):
    def helper(self,nums):
        l=len(nums)
        if l==0:
            return
        dp=[0]*l 
        x1=[0]*l
        for 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,mx2

    def getMaxMatrix(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        l1=len(matrix)
        if l1==0:
            return []
        l2=len(matrix[0])
        dp=[0]*l1
        y1=[0]*l1
        for 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]+temp
                    y1[i]=y1[i-1]
                else:
                    dp[i]=temp
                    y1[i]=i
        my2=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 0
        if 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 0
        if 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]+1
                else:
                    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]+1
                    temp=dp[i][j]
                    if temp not in dic:
                        l=i-j
                        if l>=0:
                            dic[temp]=l
                        else:
                            dic[temp]=-l 
                else:
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1])
        ops=0
        for 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]=i
        for j in range(l2+1):
            dp[0][j]=j
        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]
                else:
                    dp[i][j]=min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1
        return 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=nums


    def 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=nums


    def sumRange(self, left, right):
        """
        :type left: int
        :type right: int
        :rtype: int
        """
        dp=[0]*self.l
        for 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=matrix
        

    def sumRegion(self, row1, col1, row2, col2):
        """
        :type row1: int
        :type col1: int
        :type row2: int
        :type col2: int
        :rtype: int
        """
        s=0
        for 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=matrix
        

    def 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=0
        for x in range(row1,row2+1):
            if col1>=1:
                temp=dp[x][col2]-dp[x][col1-1]
            else:
                temp=dp[x][col2]
            s+=temp
        return 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=matrix
        

    def 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:
            return
        if 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]=False
                else:
                    if j-i<=2:
                        dp[i][j]=True
                    else:
                        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:
            return
        if 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=1
        for thelen in range(1,l+1):
            #这里一定记得是从1开始,前面那些特殊情况并没有包含它,因为这是可以在巨长的字符串里获得一个仅自己的回文串
            for i in range(l):
                j=i+thelen-1
                if j>l-1:#这里千万别不小心错写成>=,还是可以到达最长的
                    break
                if s[i]!=s[j]:
                    dp[i][j]=False
                else:
                    if j-i<=2:
                        dp[i][j]=True
                    else:
                        dp[i][j]=dp[i+1][j-1]
                
                if dp[i][j] and j-i+1>=maxlen:
                    maxlen=j-i+1
                    begin=i

        return s[begin:begin+maxlen]

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值