leetcode-记忆化深搜/动态规划v1

813:最大平均值的分组;有深搜和动态规划两种思路。深搜是自底向上,枚举分割点。动态规划是自上向下,从当前点往前找递推关系。深搜比较好理解,不用赋予初始值,因为是自底向上的,最后返回的是终点值。动态规划赋初始值的时候要小心。

5500. 乘积为正数的最长子数组长度

152. 乘积最大子数组

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        #最小的负数,最大的正数
        min_val=nums[0]
        max_val=nums[0]
        res=nums[0]
        for i in range(1,len(nums)):
            cur_max_val=max(max_val*nums[i],min_val*nums[i],nums[i])
            cur_min_val=min(min_val*nums[i],max_val*nums[i],nums[i])
            max_val=cur_max_val
            min_val=cur_min_val
            res=max(res,max_val)
        return res

53. 最大子序和

 后面的结果如果为正就对当前有增益,否则直接舍弃。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        dp=[0 for _ in range(len(nums))]
        dp[0]=nums[0]
        for i in range(1,len(nums)):
            dp[i]=max(dp[i-1]+nums[i],nums[i])
        return max(dp)

5982. 解决智力问题

 

377. 组合总和 Ⅳ

 

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        def dfs(target):
            if target in cache:
                return cache[target]
            if target==0:
                return 1
            cur_ans=0
            for i in range(len(nums)):
                if target>=nums[i]:
                    cur_ans+=dfs(target-nums[i])
            cache[target]=cur_ans
            return cur_ans
        cache={}
        return dfs(target)

1981. 最小化目标值与所选元素的差

 

 

class Solution:
    def minimizeTheDifference(self, mat: List[List[int]], target: int) -> int:
        best = 10**9+7
        m,n = len(mat),len(mat[0])
        @lru_cache(None)
        def dfs(cur,cursum):
            nonlocal best,m,n
            if best == 0:return 
            if cursum-target>=best:return 
            if cur == m:
                if abs(cursum-target)<best:
                    best = abs(cursum-target)
                return 
            for i in range(n):
                dfs(cur+1,cursum+mat[cur][i])
        dfs(0,0)
        return best

作者:dianmei
链接:https://leetcode-cn.com/problems/minimize-the-difference-between-target-and-chosen-elements/solution/you-ya-de-bao-li-python-cache-by-dianmei-o35t/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

6011. 完成比赛的最少时间

 

 力扣

class Solution:
    def minimumFinishTime(self, tires: List[List[int]], changeTime: int, numLaps: int) -> int:
        # 走一圈花费的最小时间
        min_time = min([x[0] for x in tires])
        # 遍历轮胎,计算同一个轮胎走各圈最小的时间
        step_time = []
        for f, r in tires:
            t, x = 0, 0
            # 如果走一圈的时间 > change+最快走一圈的时间,那么更换轮胎更快,继续用当前的走一定不是最优解,因此没有必要接着循环
            while f * (r ** x) <= changeTime + min_time:
                t +=  f * (r ** x)
                if x == len(step_time):
                    step_time.append(t)
                elif x < len(step_time) and step_time[x] > t:
                    step_time[x] = t
                x += 1
        # print(step_time)

        # 计算走numLaps圈所需要的最小时间
        dp = [0] * (numLaps+1)
        for i in range(1, len(dp)):
            temp = float("inf")
            # 注意这里j=0表示走1圈用的时间,对应的是i=1
            for j in range(len(step_time)):
                if i - j < 1: break
                # 如果i - j == 1, 说明是从第0圈直接走i圈的,不用加changeTime
                if i - j == 1:
                    temp = min(temp, dp[i-j-1]+step_time[j])
                else:
                    temp = min(temp, dp[i-j-1]+step_time[j]+changeTime)
            dp[i] = temp
        # print(dp)
        return dp[-1]

作者:gang-gang-hao-6
链接:https://leetcode-cn.com/problems/minimum-time-to-finish-the-race/solution/pythondong-tai-gui-hua-by-gang-gang-hao-fhy5z/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution(object):
    def minimumFinishTime(self, tires, changeTime, numLaps):
        """
        :type tires: List[List[int]]
        :type changeTime: int
        :type numLaps: int
        :rtype: int
        """
        #先求单个轮胎跑x圈的最小耗时,中间不换胎。
        #根据数据范围,最大值是15
        # 走一圈花费的最小时间
        min_time = [x[0] for x in tires]
        costx=[float("inf") for _ in range(1001)]
        for i in range(len(tires)):
            cur=tires[i]
            fi=cur[0]
            ri=cur[1]
            curtime=0
            x=1
            while fi*ri**(x-1)<=changeTime+min_time[i]:
                curtime+=fi*ri**(x-1)
                if curtime<costx[x]:
                    costx[x]=curtime
                x+=1
        # print(costx)
        dp=[0 for _ in range(numLaps+1)]
        for i in range(1,numLaps+1):
            #中间不换胎
            dp[i]=costx[i]
            #在j处换胎。最后一个轮胎连续跑了j圈,爬楼梯的思想
            for j in range(1,i+1):
                dp[i]=min(dp[i],costx[j]+dp[i-j]+changeTime)
        print(dp)
        return dp[-1]

638. 大礼包

 

 dfs+物品可重复使用

class Solution(object):
    def shoppingOffers(self, price, special, needs):
        """
        :type price: List[int]
        :type special: List[List[int]]
        :type needs: List[int]
        :rtype: int
        """
        def dfs(all_needs):
            ans = 0
            for i in range(len(all_needs)):
                # 不用礼包
                ans += price[i] * all_needs[i]
            # 使用礼包
            for j in range(len(special)):
                libao = special[j]
                # 判断是否可以使用这个礼包
                flag = True
                for h in range(len(all_needs)):
                    if libao[h] > all_needs[h]:
                        flag = False
                if not flag:
                    continue
                else:
                    # 使用当前礼包
                    price2 = libao[-1]
                    all_needs_next=[0 for _ in range(len(all_needs))]
                    for h in range(len(all_needs)):
                        all_needs_next[h] = all_needs[h] - libao[h]
                    ans = min(ans, dfs(all_needs_next) + price2)
            return ans

        return dfs(needs)

132. 分割回文串 II

 回文串dp

力扣

 

 

 

 

class Solution:
    def minCut(self, s: str) -> int:

        isPalindromic=[[False]*len(s) for _ in range(len(s))]

        for i in range(len(s)-1,-1,-1):
            for j in range(i,len(s)):
                if s[i]!=s[j]:
                    isPalindromic[i][j] = False
                elif  j-i<=1 or isPalindromic[i+1][j-1]:
                    isPalindromic[i][j] = True

        # print(isPalindromic)
        dp=[sys.maxsize]*len(s)
        dp[0]=0

        for i in range(1,len(s)):
            if isPalindromic[0][i]:
                dp[i]=0
                continue
            for j in range(0,i):
                if isPalindromic[j+1][i]==True:
                    dp[i]=min(dp[i], dp[j]+1)
        return dp[-1]

作者:carlsun-2
链接:https://leetcode-cn.com/problems/palindrome-partitioning-ii/solution/132-fen-ge-hui-wen-chuan-iidong-tai-gui-3hqva/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

没有预处理回文串的dp,超时

#dp[i]表示0到i至少分割多少次。
# leetcode submit region begin(Prohibit modification and deletion)
class Solution:
    def minCut(self, s):
        def check(a):
            left=0
            right=len(a)-1
            while left<right:
                if a[left]==a[right]:
                    left+=1
                    right-=1
                else:
                    return False
            return True
        # if check(s):
        #     return 0

        dp=[float("inf") for _ in range(len(s))]
        dp[0]=0
        for i in range(1,len(s)):
            #先判断s[0:i+1]是不是回文
            if check(s[:i+1]):
                dp[i]=0
            else:
                for j in range(i):
                    cur=s[j+1:i+1]
                    if check(cur):
                        dp[i]=min(dp[i],dp[j]+1)
        return dp[-1]

预处理回文串的dp

预处理就是把判断s[i:j](从i到j-1)是否是回文串,先用第5题的思路预处理出来。

#最长回文子串,思路:动态规划

# leetcode submit region begin(Prohibit modification and deletion)
class Solution:
    def longestPalindrome(self, s):
        max_len=0
        start=0
        end=0
        dp=[[False for _ in range(len(s))] for _ in range(len(s))]
        #初始化
        # for i in range(len(s)):
        #     dp[i][i]=True
        for i in range(len(dp)):
            #收缩j
            for j in range(i):
                if s[j]==s[i]:
                    if i-j==1 or i-j==2:
                        dp[j][i]=True
                    else:
                        dp[j][i]=dp[j+1][i-1]
                if dp[j][i]==True:
                    cur_len=i-j+1
                    if cur_len>max_len:
                        max_len=cur_len
                        start=j
                        end=i
        return s[start:end+1]
s = "ccc"
res=Solution().longestPalindrome(s)
print(res)
class Solution:
    def minCut(self, s: str) -> int:
        def check(s):
            dp = [[False for _ in range(len(s))] for _ in range(len(s))]
            #一定要加上这个初始化条件,在第五题的时候不加这个条件可以过,但是在这个题里,会有bug,因为可以用到
            for i in range(len(dp)):
                dp[i][i] = True
            for i in range(len(dp)):
                # 收缩j
                for j in range(i):
                    if s[j] == s[i]:
                        if i - j == 1 or i - j == 2:
                            dp[j][i] = True
                        else:
                            dp[j][i] = dp[j + 1][i - 1]
            return dp

        pre_dp = check(s)
        dp = [float("inf") for _ in range(len(s))]
        dp[0] = 0
        for i in range(1, len(s)):
            # 先判断0到i是不是回文
            if pre_dp[0][i]:
                dp[i] = 0
            else:
                for j in range(i):
                    if pre_dp[j + 1][i]:
                        dp[i] = min(dp[i], dp[j] + 1)
        return dp[-1]

深搜

力扣

我的深搜做法如下,枚举分割点。但是这样有例子不通过。因为这种写法总是要返回一个1,没发考虑一下情况:当在c分割时,dd子串为0

class Solution(object):
    def minCut(self, s):
        """
        :type s: str
        :rtype: int
        """
        def dfs(i):
            if i==len(s)-1:
                return 0
            res = float("inf")
            #枚举分割点
            for j in range(i,len(s)):
                if s[i:j+1] == s[i:j+1][::-1]:
                    res = min(res,dfs(j+1) + 1)
            return res
        if s == s[::-1]:
            return 0
        return dfs(0)
class Solution:
    def minCut(self, s: str) -> int:
        @lru_cache(None)
        def dfs(left, right):
            if left > right:
                return 0
            # if s[left:right + 1] == s[left:right + 1][::-1]:
            #     return 0
            if dp[left][right] == True:
                return 0
            res = float("inf")
            # 枚举分隔点
            for i in range(left, right):
                #这种写法比下面那种写法减少了重复计算
                if dp[left][i] == True:
                    res = min(res,dfs(i + 1, right) + 1)
                # res = min(res, dfs(left, i) + dfs(i + 1, right) + 1)
            return res
            # 按照第五题预处理
        def pre(s):
            dp = [[False for _ in range(len(s))] for _ in range(len(s))]
            for i in range(len(s)):
                dp[i][i] = True
            for i in range(len(s)):
                for j in range(i):
                    if s[i] == s[j]:
                        if i - j == 1 or i - j == 2:
                            dp[j][i] = True
                        else:
                            dp[j][i] = dp[j + 1][i - 1]
            return dp

        dp = pre(s)

        return dfs(0, len(s) - 1)

85 最大矩形

 

动态规划

逐个累加小块儿。最大正方形是累加边长,因为最大正方形的面积可以用边长*边长来计算。

力扣

class Solution {
public:
	int maximalRectangle(vector<vector<char>>& matrix) {
		int n = matrix.size();
		int m = 0;
		if (n > 0) { m = matrix[0].size(); }
		vector<vector<int>> heights(n + 1,vector<int>(m + 1,0));
		vector<vector<vector<int>>> dp(n + 1,vector<vector<int>>(m + 1, vector<int>(n + 1, 0)));
		int ans = 0;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				if (matrix[i-1][j-1] == '0') { continue; }
				heights[i][j] = heights[i-1][j] + 1;
				for (int k = 1; k <= heights[i][j]; k++) {
					dp[i][j][k] = dp[i][j-1][k] + k;
					ans = max(ans, dp[i][j][k]);
				}
			}
		}
		return ans;
	}
};

作者:leeyupeng
链接:https://leetcode-cn.com/problems/maximal-rectangle/solution/dong-tai-gui-hua-by-leeyupeng-5/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution(object):
    def maximalRectangle(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        #dp[i][j][k]表示以dp[i][j]为右下角,高度为k的矩形面积
        dp=[[[0 for _ in range(len(matrix))] for _ in range(len(matrix[0]))] for _ in range(len(matrix))]
        height=[[0 for _ in range(len(matrix[0]))] for _ in range(len(matrix))]
        ans=0
        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                #以(i,j)为最下端,的高度最大值
                if matrix[i][j]=='0':
                    continue
                height[i][j]=height[i-1][j]+1
                for k in range(height[i][j]):
                    dp[i][j][k]=dp[i][j-1][k]+k+1
                    ans=max(ans,dp[i][j][k])
        return ans

单调栈

221 最大正方形

 

动态规划

d​​​​​力扣

如果只考虑对角线

dp[i][j]=dp[i-1][j-1]+1,下图会考虑错。所以要同时考虑左边,上边和对角线。

class Solution(object):
    def maximalSquare(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        max_bian=0
        #dp[i][j]表示以i,j为右下角的正方形的边长
        dp=[[0 for _ in range(len(matrix[0]))] for _ in range(len(matrix))]
        #初始化最上边和最左边
        for i in range(len(matrix)):
            if matrix[i][0]=='1':
                dp[i][0]=1
                max_bian=max(max_bian,dp[i][0])
        for j in range(len(matrix[0])):
            if matrix[0][j]=='1':
                dp[0][j]=1
                max_bian=max(max_bian,dp[0][j])
        for i in range(1,len(matrix)):
            for j in range(1,len(matrix[0])):
                if matrix[i][j]=='1':
                    dp[i][j]=min(dp[i-1][j-1],dp[i][j-1],dp[i-1][j])+1
                    max_bian=max(max_bian,dp[i][j])
        return max_bian*max_bian

单调栈

1139. 最大的以 1 为边界的正方形

 力扣

思路:dp[i][j]表示以i,j为右下角的最大正方形子网络,dp[i][j]等于往min(left的最大边长,往up的最大边长),因此要用两个dp数组,一个存left的最大边长,一个存right的最大边长。这样子得到的并不是实际上的最大子网络,因为另外两个边可能有0。

解决办法:以k=min(left的最大边长,往up的最大边长)为基准,搜另外两个边,如果另外两个边有0,就缩小当前的长度。

总结:

这个题和最大矩形有异曲同工之妙,最大矩形是从右下角,往上搜。

var largest1BorderedSquare = function(grid) {
    const m = grid.length
    const n = grid[0].length
    const dpL = new Array(m+1).fill(0).map(()=>new Array(n+1).fill(0))
    const dpU = new Array(m+1).fill(0).map(()=>new Array(n+1).fill(0))
    var res = 0
    //双重循环
    for(var i = 1; i <= m; i++){ //以grid[i-1][j-1]为右下角的最大正方形的边长
        for(var j = 1; j <= n; j++){
            if(grid[i-1][j-1] === 0) continue 
            //1、以grid[i][j]结尾的连续1个数
            dpL[i][j] = dpL[i][j-1] + 1
            dpU[i][j] = dpU[i-1][j] + 1
            const len = Math.min(dpL[i][j],dpU[i][j])
            for(let k = len; k >=1; k--){
                if(dpU[i][j+1-k] >= k && dpL[i+1-k][j] >= k){
                    res = Math.max(res,k) //代表边长
                    break
                } 
            }
        }
    }
    return res*res 
};

410 分割子数组的最大值

动态规划

力扣

class Solution:
    def splitArray(self, nums: List[int], m: int) -> int:
        n = len(nums)
        f = [[10**18] * (m + 1) for _ in range(n + 1)]
        sub = [0]
        for elem in nums:
            sub.append(sub[-1] + elem)
        
        f[0][0] = 0
        for i in range(1, n + 1):
            for j in range(1, min(i, m) + 1):
                for k in range(i):
                    f[i][j] = min(f[i][j], max(f[k][j - 1], sub[i] - sub[k]))
        
        return f[n][m]

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/split-array-largest-sum/solution/fen-ge-shu-zu-de-zui-da-zhi-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
    public int splitArray(int[] nums, int m) {
        int n = nums.length;
        int[][] f = new int[n + 1][m + 1];
        for (int i = 0; i <= n; i++) {
            Arrays.fill(f[i], Integer.MAX_VALUE);
        }
        f[0][0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= Math.min(i, m); j++) {
                int s = 0;
                for (int k = i-1; k >=0; k--) {
                    s += nums[k];
                    f[i][j] = Math.min(f[i][j], Math.max(f[k][j - 1], s));
                }
            }
        }
        return f[n][m];
    }
    }

深搜

class Solution:
    def splitArray(self, nums: List[int], m: int) -> int:
        @lru_cache(None)
        def dfs(i,m):
            if i == len(nums):#无法切分子数组
                return float("inf")
            elif m == 1:#子数组即为本身
                # return sum(nums[i:])
                return presum[-1] - presum[i]
            res = float("inf")
            s = 0
            for j in range(i+1,len(nums)):
                s += nums[j-1]
                #剪枝,不加这个会超时。如果从起点到nums[j-1]的和,大于nums[j-1:],说明这个分割点已经不合格了,不用搜了
                if s > presum[-1] - presum[j-1]:
                    break
                res = min(res,max(dfs(j,m-1),s))
            return res
        presum = [0 for _ in range(len(nums)+1)]
        # presum[0] = nums[0]
        for i in range(1,len(nums)+1):
            presum[i] = presum[i-1] + nums[i-1]
        return dfs(0,m)

813. 最大平均值和的分组

 力扣

力扣

动态规划

class Solution {
    public double largestSumOfAverages(int[] nums, int k) {
        int n = nums.length;
        // dp[i][j] 表示把 nums 的前 i 项分为 j 组,得到的最大分数
        double[][] dp = new double[n+1][k+1];
        // 分成 0 组不成立。要设置为最小值,下面的dp[h-1][j - 1] + s / (i - h+1)就不会计算
        // 但是当i=0,j=0时,dp[0][0]是为0的,这个不能说不成立,可以理解为虚拟哨兵节点,为了方便遍历。
        for (int i = 1; i <= n; i++) {
            dp[i][0] = Integer.MIN_VALUE;
        }
        //i和j都得从1开始遍历,为什么呢?
        //因为,dp[0][j]表示把前0项分成j组,它是没有递推公式的,没有h。
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= Math.min(k, i); j++) {
                double s = 0.0;
                // 当前是第 j 组,前 j - 1 组是已经算好的
                for (int h = i; h>= j; h--) {   
                    s += nums[h-1];
                    dp[i][j] = Math.max(dp[i][j], dp[h-1][j - 1] + s / (i - h+1));
                }
            }
        }
        return dp[n][k];
    }
}

深搜

力扣

class Solution:
    def largestSumOfAverages(self, nums: List[int], k: int) -> float:
        @lru_cache(None)
        def dfs(i, k):
            if i == len(nums):#无法切分子数组
                return float("-inf")
            elif k == 1:#子数组即为本身
                return sum(nums[i:]) / (len(nums)-1 - i+1)
            s = 0#求和
            # j = i#剩余数组起始下标
            res = float("-inf")
            #枚举分割点,j点前面的不分割,j点及以后分割
            for j in range(i+1,len(nums)):
                s += nums[j-1]
                res = max(res, s / (j - i) + dfs(j, k - 1))
            return res
        return dfs(0, k)

6059. 检查是否有合法括号字符串路径

 

 回溯:超时

class Solution(object):
    def hasValidPath(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: bool
        """
        def check(alist):
            stack = [alist[0]]
            for i in range(1, len(alist)):
                if stack and alist[i] == stack[-1]:
                    stack.append(alist[i])
                else:
                    if stack:
                        stack.pop()
                    else:
                        stack.append(alist[i])
            if len(stack) == 0:
                return True
            return False
        directions = [(1,0),(0,1)]
        def dfs(start_x,start_y,left,right):
            if right > left:
                return False
            if start_x == len(grid)-1 and start_y == len(grid[0])-1:
                print(path)
                if check(path):
                    return True
                else:
                    return False
            for direction in directions:
                new_x = start_x + direction[0]
                new_y = start_y + direction[1]
                if new_x >= 0 and new_x <=len(grid)-1 and new_y >=0 and new_y <=len(grid[0])-1:
                    path.append(grid[new_x][new_y])
                    if grid[new_x][new_y] == "(":
                        left += 1
                    if grid[new_x][new_y] == ")":
                        right += 1
                    if dfs(new_x,new_y,left,right):
                        return True
                    path.pop()
            return False
        #特殊情况
        path = []
        path.append(grid[0][0])
        if grid[0][0] == "(":
            return dfs(0,0,1,0)
        if grid[0][0] == ")":
            return False

回溯+括号判断

只要不是带有list的回溯,是可以用lru_cache的。

注意一定要回溯,因为是在for循环里对值进行加减的

for direction in directions:
                new_x = start_x + direction[0]
                new_y = start_y + direction[1]
                if new_x >= 0 and new_x <= len(grid) - 1 and new_y >= 0 and new_y <= len(grid[0]) - 1:
                    if grid[new_x][new_y] == "(":
                        left_count += 1
                    if grid[new_x][new_y] == ")":
                        right_count += 1

不回溯的话for循环会对left_count和right_count进行覆盖。

class Solution:
    def hasValidPath(self, grid: List[List[str]]) -> bool:
        directions = [(1,0),(0,1)]
        @lru_cache(None)
        def dfs(start_x,start_y,left_count,right_count):
            if right_count > left_count:
                return False
            if start_x == len(grid) - 1 and start_y == len(grid[0]) - 1:
                if left_count == right_count:
                    return True
                else:
                    return False
            for direction in directions:
                new_x = start_x + direction[0]
                new_y = start_y + direction[1]
                if new_x >= 0 and new_x <= len(grid) - 1 and new_y >= 0 and new_y <= len(grid[0]) - 1:
                    if grid[new_x][new_y] == "(":
                        left_count += 1
                    if grid[new_x][new_y] == ")":
                        right_count += 1
                    if dfs(new_x, new_y, left_count,right_count):
                        return True
                    if grid[new_x][new_y] == "(":
                        left_count -= 1
                    if grid[new_x][new_y] == ")":
                        right_count -= 1
            return False
        if grid[0][0] == ")":
            return False
        return dfs(0,0,1,0)

记忆化深搜+括号判断

手把手解决三道括号相关的算法题

只有圆括号的情况下,不必用栈。判断方法是

左括号的数量最后与右括号相等,并且任意位置左括号数量一定不小于右括号数量。

 不用回溯写法的话,不能在for循环里改变dfs的参数值

class Solution:
    def hasValidPath(self, grid: List[List[str]]) -> bool:
        @lru_cache(None)
        def dfs(i, j, c):
            if c < 0 or i == m or j == n:#当数量小于0或者超出边界则不合法
                return False
            c = c + 1 if grid[i][j] == "(" else c - 1#更新数量
            return c == 0 if i == m - 1 and j == n - 1 else dfs(i + 1, j, c) or dfs(i, j + 1, c) #如果已经到了右下角则根据数量返回是否合法,否则继续深搜
        m, n = len(grid), len(grid[0])
        return dfs(0, 0, 0)

作者:qin-qi-shu-hua-2
链接:https://leetcode-cn.com/problems/check-if-there-is-a-valid-parentheses-string-path/solution/python-ji-yi-hua-shen-sou-by-qin-qi-shu-5yhnr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

6069. 最大波动的子字符串

 最大子数组中的变形

class Solution:
    def largestVariance(self, s: str) -> int:
        if s.count(s[0]) == len(s):
            return 0
        ans = 0
        for a in ascii_lowercase:
            for b in ascii_lowercase:
                if b == a:
                    continue
                diff, diff_with_b = 0, -inf
                for ch in s:
                    if ch == a:
                        diff += 1
                        diff_with_b += 1
                    elif ch == b:
                        diff -= 1
                        diff_with_b = diff  # 记录包含 b 时的 diff
                        if diff < 0:
                            diff = 0
                    if diff_with_b > ans:
                        ans = diff_with_b
        return ans

作者:endlesscheng
链接:https://leetcode.cn/problems/substring-with-largest-variance/solution/by-endlesscheng-5775/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer II 112. 最长递增路径

 有递增条件不用加visited

class Solution:
    def longestIncreasingPath(self, matrix: List[List[int]]) -> int:
        directions = [(0,1),(0,-1),(1,0),(-1,0)]
        @lru_cache(None)
        def dfs(i,j):
            cur_res = 1
            for direction in directions:
                new_x = i + direction[0]
                new_y = j + direction[1]
                if 0<=new_x<=len(matrix)-1 and 0<=new_y<=len(matrix[0])-1 and matrix[new_x][new_y] > matrix[i][j]:
                    cur_res = max(cur_res,dfs(new_x,new_y)+1)
            return cur_res
        res = 0
        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                cur = dfs(i,j)
                # print(i,j,cur)
                res = max(cur,res)
        return res

 6110. 网格图中递增路径的数目

class Solution:
    def countPaths(self, grid: List[List[int]]) -> int:
        directions = [(-1,0),(1,0),(0,-1),(0,1)]
        @lru_cache(None)
        def dfs(row,col):
            res = 1
            for direction in directions:
                new_row = row + direction[0]
                new_col = col + direction[1]
                if 0 <= new_row < len(grid) and 0 <= new_col < len(grid[0]):
                    if grid[new_row][new_col] > grid[row][col]:
                        #注意这里不要+1,因为求的是数量
                        res += dfs(new_row,new_col)
            return res % (10**9+7)
        count = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                #以i,j为起点,的路径数目
                count += dfs(i,j)
        return count % (10**9+7)

6109. 知道秘密的人数

class Solution:
    def peopleAwareOfSecret(self, n: int, delay: int, forget: int) -> int:
        # 使用动态规划dp[i]表示第i+1天新知道秘密的人数
        dp = [0]*n
        dp[0] = 1
        for i in range(n):
            # 同时dp[i]能够在未来i+delay到min(i+forget, n)增加传播
            for j in range(i+delay, min(i+forget, n)):
                dp[j] += dp[i]
        # 返回最后一天还没有忘记秘密的人数即前forget天新知道秘密的人数
        return sum(dp[-forget:]) % (10**9+7)

作者:liupengsay
链接:https://leetcode.cn/problems/number-of-people-aware-of-a-secret/solution/er-xu-cheng-ming-jiu-xu-zui-by-liupengsa-bg37/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

6203. 矩阵中和能被 K 整除的路径

 

按余数分类动态规划

2400. 恰好移动 k 步到达某一位置的方法数目

 

class Solution:
    def numberOfWays(self, startPos: int, endPos: int, k: int) -> int:
        @lru_cache(None)
        def dfs(pos,cur):
            if pos == endPos and cur == 0:
                return 1
            if cur == 0:
                return 0
            return dfs(pos + 1,cur - 1) + dfs(pos -1 ,cur-1)
        return dfs(startPos,k) % int(1e9 + 7)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值