leetcode-前缀和/差分数组

5972. 统计隐藏数组数目

 先求出来这条线,然后统一往上走直到upper,往下走直到lower。这个上下能波动几次,就是最终返回值

 其实就是在算这个曲线的高低差是多少

class Solution:
    def numberOfArrays(self, differences: List[int], lower: int, upper: int) -> int:
        value=0
        min_val=0
        max_val=0
        for i in range(len(differences)):
            value=value+differences[i]
            min_val=min(min_val,value)
            max_val=max(max_val,value)
        if upper-lower-(max_val-min_val)+1>0:
            return upper-lower-(max_val-min_val)+1
        return 0

前缀和还原差分数组

class Solution(object):
    def numberOfArrays(self, differences, lower, upper):
        """
        :type differences: List[int]
        :type lower: int
        :type upper: int
        :rtype: int
        """
        #前缀和还原差分数组
        arr = [0]
        for i in range(len(differences)):
            arr.append(arr[-1]+differences[i])
        max_val = max(arr)
        min_val = min(arr)
        left = lower - min_val
        right = upper - max_val
        if right < left:
            return 0
        return right-left+1

5955 摘水果

前缀和

力扣https://leetcode-cn.com/problems/maximum-fruits-harvested-after-at-most-k-steps/solution/qiu-chu-fan-wei-nei-qian-zhui-he-zui-da-tfeoy/

class Solution(object):
    def maxTotalFruits(self, fruits, startPos, k):
        """
        :type fruits: List[List[int]]
        :type startPos: int
        :type k: int
        :rtype: int
        """
        n = len(fruits)
        postions = [fruit[0] for fruit in fruits]
        amounts = [fruit[1] for fruit in fruits]

        # 前缀和便于统计不同区间范围总和
        preSum = [0] * (n + 1)
        for i in range(1, n + 1):
            preSum[i] = preSum[i - 1] + amounts[i - 1]
        
        ret = float('-inf')
        for x in range(k + 1):
            y = k - 2*x
            # 往左 x 步, 往右 y 步
            left, right = startPos - x, startPos + y
            # 确定区间边界
            leftIdx, rightIdx = bisect_left(postions, left), bisect_right(postions, right)
            ret = max(ret, preSum[rightIdx] - preSum[leftIdx])

            # 往左 y 步, 往右 x 步
            left, right = startPos - y, startPos + x
            leftIdx, rightIdx = bisect_left(postions, left), bisect_right(postions, right)
            ret = max(ret, preSum[rightIdx] - preSum[leftIdx])
        return ret

上面那个代码应该有点问题,在确定左边界之后,右边界应该是k-2*左边届。

2055. 蜡烛之间的盘子

 

 前缀和+二分找边界

class Solution:
    def platesBetweenCandles(self, s: str, queries: List[List[int]]) -> List[int]:
        #双指针找到i到j之间间隔最长的蜡烛[h1,h2],前缀和找到[h1,h2]之间盘子数量

        #前缀和
        presum=[0 for _ in range(len(s))]
        if s[0]=='*':
            presum[0]=1
        for i in range(1,len(s)):
            if s[i]=='*':
                presum[i]=presum[i-1]+1
            else:
                presum[i]=presum[i-1]
        
        #二分找边界
        res=[]
        A = [i for i,char in enumerate(s) if char=='|']
        for i in range(len(queries)):
            l=queries[i][0]
            r=queries[i][1]
            left=bisect_left(A,l)
            right=bisect_right(A,r)-1
            if left<right:
            # while s[left]=='*' and left<right:
            #     left+=1
            # while s[right]=='*' and left<right:
            #     right-=1
                count=presum[A[right]]-presum[A[left]]
                res.append(count)
            else:
                res.append(0)
        return res
class Solution:
    def platesBetweenCandles(self, s: str, queries):
        presum = [0 for _ in range(len(s))]
        if s[0] == '*':
            presum[0] = 1
        for i in range(1, len(s)):
            if s[i] == '*':
                presum[i] = presum[i - 1] + 1
            else:
                presum[i] = presum[i - 1]

        def find_left(alist,l):
            left=0
            right=len(alist)-1
            while left<right:
                mid=(left+right)//2
                if alist[mid]>=l:
                    right=mid
                else:
                    left=mid+1
            return right
        def find_right(alist,r):
            left = 0
            right = len(alist) - 1
            while left < right:
                mid = (left + right+1) // 2
                if alist[mid]<=r:
                    left = mid
                else:
                    right = mid-1
            return right
        # 二分找边界
        res = []
        # 蜡烛的index
        alist = [i for i, char in enumerate(s) if char == '|']
        print(alist)
        for i in range(len(queries)):
            # l和r都是index
            l = queries[i][0]
            r = queries[i][1]
            # 找l右边第一个index
            pos1 = find_left(alist, l)
            pos2 = find_right(alist, r)
            if pos1 < pos2:
                count = presum[alist[pos2]] - presum[alist[pos1]]
                res.append(count)
            else:
                res.append(0)
        return res
# 输入:s = "**|**|***|", queries = [[2,5],[5,9]]
# # 输出:[2,3]
s = "**|**|***|"
queries = [[2,5],[5,9]]
res=Solution().platesBetweenCandles(s,queries)
print(res)
class Solution {
public:
    vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
     //预处理+二分
    //建立一个数组 放满蜡烛的方位
    //对于每次查询 二分找到 其对应的下标 
    //返回 两坐标在s上的之差-1-(在数组上的之差-1)=s上之差减去数组上只差
    vector<int> candles;
    int len=s.size();
    for(int i=0;i<len;i++){
        if(s[i]=='|') candles.emplace_back(i);
    }
    vector<int> ans;
    len=queries.size();
    for(int i=0;i<len;i++){
        if(candles.size()==0){
            ans.emplace_back(0);
            continue;
        }
        int left=queries[i][0],right=queries[i][1];
        int left_index=binary_search(candles,left,true),
            right_index=binary_search(candles,right,false);
        if(left_index>=right_index) ans.emplace_back(0);
        else  ans.emplace_back(candles[right_index]-candles[left_index]-(right_index-left_index));
    }
    return ans;
    }
    //对于right 找到靠左的
    //对于left  找到靠右的
    int binary_search(vector<int>& aim,int cur,bool flag){
        int left=0,right=aim.size()-1;
        while(left<right){
           int mid=(right-left)/2+left;
           if(aim[mid]==cur) return mid;
           if(aim[mid]>cur){
               right=mid-1;
           }else{
               left=mid+1;
           }
        }
        if(!flag && left>0 && aim[left]>cur) left--;
        if(flag && left<aim.size()-1 && aim[left]<cur) left++;
        return left;
    }
};
class Solution(object):
    def platesBetweenCandles(self, s, queries):
        """
        :type s: str
        :type queries: List[List[int]]
        :rtype: List[int]
        """
        # 前缀和,二分找边界
        presum = [0 for _ in range(len(s))]
        if s[0] == "*":
            presum[0] = 1
        for i in range(1, len(s)):
            if s[i] == "*":
                presum[i] = presum[i - 1] + 1
            else:
                presum[i] = presum[i - 1]
        #二分查找的数组要有单调性,得是查找index
        arr_index = []
        for i in range(len(s)):
            if s[i] == "|":
                arr_index.append(i)
        if len(arr_index) == 0:
            return [0 for _ in range(len(queries))]

        res = []
        for i in range(len(queries)):
            a1 = queries[i][0]
            a2 = queries[i][1]
            #找不到蜡烛的情况处理
            if a1 > arr_index[-1] or a2 < arr_index[0]:
                res.append(0)
                continue
            # 找a1右边最靠近a1的蜡烛
            left = 0
            right = len(arr_index) - 1
            while left <= right:
                mid = (left + right) // 2
                if arr_index[mid] >= a1:
                    right = mid - 1
                else:
                    left = mid + 1
            b1 = arr_index[right + 1]
            # 找a2左边最靠近的蜡烛
            left = 0
            right = len(arr_index) - 1
            while left <= right:
                mid = (left + right) // 2
                if arr_index[mid] <= a2:
                    left = mid + 1
                else:
                    right = mid - 1
            b2 = arr_index[left - 1]
            if b1 <= b2:
                cur = presum[b2] - presum[b1]
            else:
                cur = 0
            res.append(cur)
        return res

前缀和+双指针找边界,超时

我是用的前缀和+双指针找边界,超时

class Solution:
    def platesBetweenCandles(self, s: str, queries: List[List[int]]) -> List[int]:
        #双指针找到i到j之间间隔最长的蜡烛[h1,h2],前缀和找到[h1,h2]之间盘子数量

        #前缀和
        presum=[0 for _ in range(len(s))]
        if s[0]=='*':
            presum[0]=1
        for i in range(1,len(s)):
            if s[i]=='*':
                presum[i]=presum[i-1]+1
            else:
                presum[i]=presum[i-1]
        
        #双指针找
        res=[]
        for i in range(len(queries)):
            left=queries[i][0]
            right=queries[i][1]
            while s[left]=='*' and left<right:
                left+=1
            while s[right]=='*' and left<right:
                right-=1
            count=presum[right]-presum[left]
            res.append(count)
        return res

724. 寻找数组的中心下标

前缀和

前缀和+后缀和

class Solution:
    def pivotIndex(self, nums):
        presum = [0 for _ in range(len(nums)+1)]
        postsum = [0 for _ in range(len(nums)+1)]
        for i in range(1,len(presum)):
            presum[i] = presum[i-1] + nums[i-1]
        for i in range(len(nums)-2,-1,-1):
            postsum[i] = postsum[i+1] + nums[i+1]
        print(presum)
        print(postsum)
        for i in range(len(nums)):
            if presum[i] == postsum[i]:
                return i
        return -1
# nums = [1, 7, 3, 6, 5, 6]
# nums = [1, 2,3]
nums = [-1,-1,-1,1,1,1]
res = Solution().pivotIndex(nums)
print(res)

2163 删除元素后和的最小差值

前缀和+后缀和+堆

class Solution:
    def minimumDifference(self, nums: List[int]) -> int:
        m = len(nums)
        n = m // 3

        min_pq = nums[m - n:]
        heapify(min_pq)
        suf_max = [0] * (m - n + 1)  # 后缀最大和
        suf_max[-1] = s = sum(min_pq)
        for i in range(m - n - 1, n - 1, -1):
            s += nums[i] - heappushpop(min_pq, nums[i])
            suf_max[i] = s

        max_pq = [-v for v in nums[:n]]  # 所有元素取反当最大堆
        heapify(max_pq)
        pre_min = -sum(max_pq)  # 前缀最小和
        ans = pre_min - suf_max[n]
        for i in range(n, m - n):
            pre_min += nums[i] + heappushpop(max_pq, -nums[i])
            ans = min(ans, pre_min - suf_max[i + 1])
        return ans

作者:endlesscheng
链接:https://leetcode-cn.com/problems/minimum-difference-in-sums-after-removal-of-elements/solution/qian-zhui-zui-xiao-he-hou-zhui-zui-da-he-yz3d/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
    public long minimumDifference(int[] nums) {
        int n = nums.length / 3;
        // min[i] 表示 nums[0] ~ nums[i] 个元素中选择 n 个元素和最小
        long[] min = new long[3 * n];

        // max[i] 表示 nums[i] ~ nums[3*n-1] 个元素中选择 n 个元素和最大
        long[] max = new long[3 * n];

        // 升序队列、 降序队列
        PriorityQueue<Integer> asc = new PriorityQueue<>();
        PriorityQueue<Integer> desc = new PriorityQueue<>((a, b) -> b - a);
        
        long sum_first = 0, sum_second = 0;
        for (int i = 0; i < 3 * n; i++) {
            int l = i, r = 3 * n - 1 - i;
            int left = nums[l], right = nums[r];
            sum_first += left;
            desc.add(left);

            sum_second += right;
            asc.add(right);

            if (i >= n) {
                sum_first -= desc.poll();
                sum_second -= asc.poll();
            }
            min[l] = sum_first;
            max[r] = sum_second;
        }

        // 遍历所有情况找到最小差值
        long minAns = Long.MAX_VALUE;
        for (int i = n - 1; i < 2 * n; i++) {
            minAns = Math.min(minAns, min[i] - max[i + 1]);
        }

        return minAns;
    }
}

 795. 区间子数组个数

 暴力双指针,超时

class Solution:
    def numSubarrayBoundedMax(self, nums: List[int], left: int, right: int) -> int:
         #从i到j的子数组是否满足条件
        #这么写有一个错误:当前index不满足时,还可以往后遍历
        cnt=0
        index1=0
        while index1<len(nums):
            index2=index1
            max_val=nums[index2]
            while max_val<left:
                index2=index2+1
                if index2 < len(nums):
                    max_val= max(max_val, nums[index2])
                else:
                    break
            while max_val>=left and max_val<=right:
                cnt+=1
                index2=index2+1
                if index2 < len(nums):
                    max_val = max(max_val, nums[index2])
                else:
                    break
            index1+=1
        return cnt

前缀和mostk

力扣

class Solution:
    def numSubarrayBoundedMax(self, nums: List[int], left: int, right: int) -> int:
        #前缀和,拆分成小于right的子数组和小于left的子数组
        def mostk(k):
            #以右端点为基准
            ans=0
            pre=0
            for i in range(len(nums)):
                if nums[i]<=k:
                    pre=pre+1
                else:
                    pre=0 
                ans=ans+pre 
            return ans 
        return mostk(right)-mostk(left-1)

1109. 航班预订统计

前缀和还原差分数组

对于航班预订系统的理解:

前缀和还原差分数组,比如bookings=[[1,2,10]]
n = 5,有5个航班,在第一个航班上来10个人,,第三个航班-10. 差分数组为[10, 0, -10, 0, 0],最后结果是[10, 10, 0, 0, 0]。

第三个航班-10,说明从第三个航班开始,没有第一个航班上带来的增量了

 

class Solution(object):
    def corpFlightBookings(self, bookings, n):
        """
        :type bookings: List[List[int]]
        :type n: int
        :rtype: List[int]
        """
        #转化为上车下车问题
        res=[0 for _ in range(n)]
        for i in range(len(bookings)):
            cur_first=bookings[i][0]
            cur_last=bookings[i][1]
            res[cur_first-1]+=bookings[i][2]
            if cur_last<n:
                res[cur_last]-=bookings[i][2]

        for i in range(1,n):
            res[i]=res[i-1]+res[i]
        return res

        # for i in range(len(bookings)):
        #     cur=bookings[i]
        #     cur_first=cur[0]
        #     cur_last=cur[1]
        #     for i in range(cur_first,cur_last+1):
        #         res[i-1]+=cur[2]
        return res

253. 会议室 II

 前缀和还原差分数组

 

560. 和为 K 的子数组

 前缀和+哈希表

力扣

暴力

 前缀和

 前缀和+哈希表

注意:实现的时候要先判断差值是否在哈希表中 ,然后再把当前值加入哈希表。先都加入哈希表,再判断差值是否在哈希表中,有case无法通过,如:。因为无法判断减去的值是不是当前自身值

class Solution(object):
    def subarraySum(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        # 前缀和
        presum = [0 for _ in range(len(nums))]
        maps={}
        presum[0] = nums[0]
        maps[presum[0]]=1
        for i in range(1, len(nums)):
            presum[i] = presum[i - 1] + nums[i]
            if presum[i] in maps:
                maps[presum[i]]+=1
            else:
                maps[presum[i]]=1
        count = 0
        for i in range(len(nums)):
            if presum[i]==k:
                count+=1
            if presum[i]-k in maps:
                count+=maps[presum[i]-k]
        return count
           

        # count=0
        # for i in range(len(nums)):
        #     for j in range(i+1,len(nums)+1):
        #         cur=nums[i:j]
        #         if sum(cur)==k:
        #             count+=1
        return count

1124.表现良好的最长时间段

前缀和+单调栈

6035. 选择建筑的方案数

前缀和+后缀和

20220403周赛的一道题,开始思路想错了,以为是01背包,加了cache之后只能通过一半的case.

背包如下:

class Solution:
    def numberOfWays(self, s: str) -> int:
        @lru_cache(None)
        def dfs(index,count,pre):
            if index == len(s) and count!=0:
                return 0
            elif count == 0:
                return 1
            #如果选当前的index
            a1,a2 = 0,0
            if s[index]!=pre and count >= 1:
                a1 = dfs(index+1,count-1,s[index])
            #如果不选当前的index
            a2 = dfs(index+1,count,pre)
            return a1+a2
        return dfs(0,3,"2")
class Solution(object):
    def numberOfWays(self, s):
        """
        :type s: str
        :rtype: int
        """
        #只有101,和010两种情况。
        #遍历1次,找当前0前面有多少个1,当前0后面多少个1.前缀和,后缀和,注意下标
        #遍历1次,找当前1前面多少个0,当前1后面多少个0.前缀和,后缀和,注意下标
        def find_pre_post(value):
            presum = [0 for _ in range(len(s))]
            postsum = [0 for _ in range(len(s))]
            if s[0] == value:
                presum[0] = 1
            if s[-1] == value:
                postsum[-1] = 1
            for i in range(1,len(s)):
                if s[i] == value:
                    presum[i] = presum[i-1] + 1
                else:
                    presum[i] = presum[i-1]
            for i in range(len(s)-2,-1,-1):
                if s[i] == value:
                    postsum[i] = postsum[i+1] + 1
                else:
                    postsum[i] = postsum[i+1]
            return presum,postsum
        presum1,postsum1 = find_pre_post("1")
        presum0,postsum0 = find_pre_post("0")
        count = 0
        for i in range(len(s)):
            if s[i] == "0":
                count += presum1[i]*postsum1[i]
            if s[i] == "1":
                count += presum0[i]*postsum0[i]
        return count

6072. 转角路径的乘积中最多能有几个尾随零

 

数学+前缀和+后缀和 

0417周赛t3,我用的是深搜,走路径。超时

class Solution:
    def maxTrailingZeros(self, grid: List[List[int]]) -> int:
        def cal_0(num):
            count = 0
            num_str = str(num)
            for h in range(len(num_str)-1,-1,-1):
                if num_str[h] == "0":
                    count += 1
                else:
                    break
            return count
        #visited存储已经走过的方向,小于等于2时说明转弯最多1次
        dires = [(-1,0),(1,0),(0,-1),(0,1)]
        def dfs(start_x,start_y,visited):
            if start_x < 0 or start_x > len(grid)-1 or start_y < 0 or  start_y > len(grid[0])-1 or grid[start_x][start_y] < 0:
                return [1]
            tmp = grid[start_x][start_y]
            grid[start_x][start_y] = -1
            res_sum = []
            for new_x,new_y in dires:
                if len(visited) > 0 and visited[-1] != (new_x,new_y):
                    visited.append((new_x,new_y))
                    if len(visited) <= 2:
                        tmp1 = dfs(start_x + new_x, start_y + new_y, visited)
                        for t in tmp1:
                            cur = t * tmp
                            res_sum.append(cur)
                    visited.pop()
                elif len(visited) > 0 and visited[-1] == (new_x,new_y):
                    tmp1 = dfs(start_x + new_x, start_y + new_y, visited)
                    for t in tmp1:
                        cur = t * tmp
                        res_sum.append(cur)
                elif len(visited) == 0:
                    visited.append((new_x, new_y))
                    tmp1 = dfs(start_x + new_x, start_y + new_y, visited)
                    for t in tmp1:
                        cur = t * tmp
                        res_sum.append(cur)
                    visited.pop()
            grid[start_x][start_y] = tmp
            return res_sum
        res = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                str_num = str(grid[i][j])
                if str_num[-1] in ('1','3','7','9'):
                    continue
                cur_ans = dfs(i, j,[])
                for c in cur_ans:
                     cur_ans1 = cal_0(c)
                     res = max(res,cur_ans1)
        return res

力扣

class Solution {
    public int maxTrailingZeros(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int[][] r2 = new int[m][n];     // r - row
        int[][] c2 = new int[m][n];     // c - column
        int[][] r5 = new int[m][n];
        int[][] c5 = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int num = grid[i][j];
                int n2 = getFactorNum(num, 2), n5 = getFactorNum(num, 5);
                r2[i][j] = j > 0 ? r2[i][j - 1] + n2 : n2;
                c2[i][j] = i > 0 ? c2[i - 1][j] + n2 : n2;
                r5[i][j] = j > 0 ? r5[i][j - 1] + n5 : n5;
                c5[i][j] = i > 0 ? c5[i - 1][j] + n5 : n5;
            }
        }

        int ans = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 左上
                int cnt2 = (j > 0 ? r2[i][j - 1] : 0) + c2[i][j];
                int cnt5 = (j > 0 ? r5[i][j - 1] : 0) + c5[i][j];
                ans = Math.max(ans, Math.min(cnt2, cnt5));
                // 右上
                cnt2 = r2[i][n - 1] - r2[i][j] + c2[i][j];
                cnt5 = r5[i][n - 1] - r5[i][j] + c5[i][j];
                ans = Math.max(ans, Math.min(cnt2, cnt5));
                // 左下
                cnt2 = r2[i][j] + c2[m - 1][j] - c2[i][j];
                cnt5 = r5[i][j] + c5[m - 1][j] - c5[i][j];
                ans = Math.max(ans, Math.min(cnt2, cnt5));
                // 右下
                cnt2 = r2[i][n - 1] - (j > 0 ? r2[i][j - 1] : 0) + c2[m - 1][j] - c2[i][j];
                cnt5 = r5[i][n - 1] - (j > 0 ? r5[i][j - 1] : 0) + c5[m - 1][j] - c5[i][j];
                ans = Math.max(ans, Math.min(cnt2, cnt5));
            }
        }
        return ans;
    }

    private int getFactorNum(int n, int factor) {
        int cnt = 0;
        while (n % factor == 0) {
            n /= factor;
            cnt++;
        }
        return cnt;
    }
}

作者:inuter
链接:https://leetcode-cn.com/problems/maximum-trailing-zeros-in-a-cornered-path/solution/-by-inuter-q1at/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

一维前缀和 

计算每一列的前缀和

class NumMatrix:

    def __init__(self, matrix: List[List[int]]):
        self.pre_sum_col = [[0 for _ in range(len(matrix[0]))] for _ in range(len(matrix))]
        #初始化第一行
        for j in range(len(matrix[0])):
            self.pre_sum_col[0][j] = matrix[0][j]
        for i in range(1,len(matrix)):
            for j in range(len(matrix[0])):
                self.pre_sum_col[i][j] = self.pre_sum_col[i-1][j] + matrix[i][j]


    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        #以列为聚合计算前缀和,一列一列的计算
        res = 0
        for col in range(col1,col2+1):
            if row1 > 0:
                res += self.pre_sum_col[row2][col]-self.pre_sum_col[row1-1][col]
            else:
                res += self.pre_sum_col[row2][col]
        return res

 二维前缀和

class NumMatrix:

    def __init__(self, matrix: List[List[int]]):
        self.presum = [[0 for _ in range(len(matrix[0]))] for _ in range(len(matrix))]
        # 初值
        self.presum[0][0] = matrix[0][0]
        for i in range(1, len(matrix)):
            self.presum[i][0] = self.presum[i - 1][0] + matrix[i][0]
        for j in range(1, len(matrix[0])):
            self.presum[0][j] = self.presum[0][j - 1] + matrix[0][j]

        for i in range(1, len(matrix)):
            for j in range(1, len(matrix[0])):
                self.presum[i][j] = self.presum[i - 1][j] + self.presum[i][j - 1] - self.presum[i - 1][j - 1] + \
                                    matrix[i][j]


    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        if row1 >= 1 and col1 >= 1:
            res = self.presum[row2][col2] - self.presum[row2][col1 - 1] - self.presum[row1 - 1][col2] + \
                  self.presum[row1 - 1][col1 - 1]
        elif row1 >= 1 and col1 == 0:
            res = self.presum[row2][col2] - self.presum[row1 - 1][col2]
        elif row1 == 0 and col1 >= 1:
            res = self.presum[row2][col2] - self.presum[row2][col1 - 1]
        else:
            res = self.presum[row2][col2]
        return res

363. 矩形区域不超过 K 的最大数值和

 力扣

 

class Solution {
    public int maxSumSubmatrix(int[][] mat, int k) {
        int m = mat.length, n = mat[0].length;

        // 预处理前缀和
        int[][] sum = new int[m + 1][n + 1];
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + mat[i - 1][j - 1];
            }
        }

        int ans = Integer.MIN_VALUE;
        // 遍历子矩阵的上边界
        for (int top = 1; top <= m; top++) {
            // 遍历子矩阵的下边界
            for (int bot = top; bot <= m; bot++) {
                // 使用「有序集合」维护所有遍历到的右边界
                TreeSet<Integer> ts = new TreeSet<>();
                ts.add(0);
                // 遍历子矩阵的右边界
                for (int r = 1; r <= n; r++) {
                    // 通过前缀和计算 right
                    int right = sum[bot][r] - sum[top - 1][r];
                    // 通过二分找 left
                    Integer left = ts.ceiling(right - k);
                    if (left != null) {
                        int cur = right - left;
                        ans = Math.max(ans, cur);
                    }
                    // 将遍历过的 right 加到有序集合
                    ts.add(right);
                }
            }
        }
        return ans;
    }
}

作者:AC_OIer
链接:https://leetcode-cn.com/problems/max-sum-of-rectangle-no-larger-than-k/solution/gong-shui-san-xie-you-hua-mei-ju-de-ji-b-dh8s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值