jjzjj

蓝桥杯刷题016——最大子矩阵(尺取法+单调队列)

让机器理解语言か 2023-08-18 原文

题目来源:最大子矩阵 - 蓝桥云课 (lanqiao.cn)

问题描述

小明有一个大小为 N×M 的矩阵, 可以理解为一个 N 行 M 列的二维数组。

我们定义一个矩阵 m 的稳定度 f(m) 为f(m)=max(m)−min(m), 其中 max(m) 表示矩阵 m 中的最大值, min(m) 表示矩阵 m 中的最小值。

现在小明想要从这个矩阵中找到一个稳定度不大于 limit 的子矩阵, 同时他还希望这个子矩阵的面积越大越好 (面积可以理解为矩阵中元素个数)。

子矩阵定义如下: 从原矩阵中选择一组连续的行和一组连续的列, 这些行列交点上的元素组成的矩阵即为一个子矩阵。

输入格式

第一行输入两个整数 N,M, 表示矩阵的大小

接下来 N 行, 侮行输入 M 个整数,表示这个矩阵

最后一行输入一个整数 limit, 表示限制

辎出格式

输出一个整数. 分别表示小明选择的子矩阵的最大面积

样例输入

3 4
2 0 7 9
0 6 9 7
8 4 6 4
8

样例输出

6

样例说明

满足稳定度不大于 8 的且面积最大的子矩阵总共有三个, 他们的面积都是 6 (粗体表示子矩阵元素)

2 0 7 9

0 6 9 7

8 4 6 4             

2 0 7 9

0 6 9 7

8 4 6 4

2 0 7 9

6 9 7

4 6 4

评测用例规模与约定

对于所有评测用例, 0≤0≤ 矩阵元素值, limit ≤105≤105 。

题目大意

给定一个N*M的二维矩阵和一个数字limlit,求出符合稳定度f(m)<=limit面积最大的子矩阵
f(m)为当前子矩阵的最大值-最小值

解法一:暴力法(通过30%)

        通过四重循环(四条边)枚举每个子矩阵,然后再计算当前子矩阵的最大值和最小值之差,若差值满足<=limit,则维护更新面积最大值。
算法复杂度大于O(n^4)

解法二: 尺取法+单调队列(通过100%)

解题关键(三问):

1、如何较为快速地确定当前子矩阵的大小?

双指针算法(尺取法)

【图解】尺取法 

1、一开始左右指针都是从左端点开始 

 

 2、右指针先向右移动

 

3、右指针移动到刚好满足f(m)<=limit 的临界位置

 

4、右指针再向右移动,这时候在区间[l, r']就不满足 f(m)<=limit 

 

5、此时开始移动左指针 

 

6、当左指针移动到刚好满足f(m)<=limit 的临界位置时,右指针开始移动,这样周而复始,直到右指针达到终点。

 

 

        观察数据,可发现行的数据范围较小,列的数据范围较大,因此子矩阵的上下边可以通二.重遍历确定,复杂度,子矩阵的左右边采用双指针算法进行确定,复杂度
算法复杂度

  

2、如何较为快速地找出当前子矩阵的两个最值?

        每次移动左右指针,都会后一整列的数字进入或退出当前子矩阵,我们需要先计算出当前列[up,down]范围的最值优化:通过转置矩阵,我们可以利用切片的方式,不用循环求出原矩阵每一列[up,down]范围内的最值

  

3、当前子矩阵实在不断移动的,如何在移动过程中维护最值?

        使用两个队列分别记录当前子矩阵的最大值和最小值,维护队列的单调性,双指针移动过程中时刻更新队列。

单调队列

维护最大值的单调队列示例:

         第一个数字直接加入队列,后面每一列如果出现的数字比队列最后一个元素小,则加入队列,否则去掉队列最后一个元素, 再与队列最后一个比较,直到队列为空或者该数字比队列最后一个元素小再将该数字加入队列。

 

维护最大值的单调队列,内部元素是递减排列的

维护最小值的单调队列,内部元素是递增排列的

解题思路:双指针+单调队列

1、二重循环对矩阵的上下边界进行遍历
2、对于每个确定的上下边界up,down,左右边界采用双指针算法确定

3、使用两个队列分别记录当前子矩阵的最大值和最小值,维护队列的单调性,双指针移动过程中时刻更新队列
4、初始res=0保存结果,双指针移动过程中维护最大值

代码演示

from collections import deque
n, m = map(int, input().split())
mat = [list(map(int, input().split())) for _ in range(n)]   # 二维列表存矩阵:n行m列
lim = int(input())
max_res = 0

# 转置矩阵    m行n列  
remat = [[0] * n for _ in range(m)]
for i in range(m):                                          
    for j in range(n):
        remat[i][j] = mat[j][i]

for up in range(n):                # 两个for循环遍历上下边
    for down in range(up, n):
        l = 0    # 左指针初始化
        maxq = deque() # 最大值的单调队列
        minq = deque() # 最小值的单调队列
        #maxq.append(max(remat[l][up: down + 1]))    
        #minq.append(min(remat[l][up: down + 1]))
        for r in range(0, m):
            newmax = max(remat[r][up: down + 1])  # 右边新的一列l:[up,down]最大值
            while len(maxq) and maxq[-1] < newmax:# 最大值的单调队列的最后一个比新的最大值小
                maxq.pop()                        # 删除队列最后一个
            maxq.append(newmax)                   # 把新的一列的最大值放入队列
            newmin = min(remat[r][up: down + 1])  
            while len(minq) and minq[-1] > newmin:
                minq.pop()
            minq.append(newmin)
            cumax, cumin = maxq[0], minq[0]       # 子矩阵的最大值和最小值
            while cumax - cumin > lim and l < r:  # 最大-最小>lim,左指针<右指针
                if len(maxq) and max(remat[l][up: down + 1]) == maxq[0]: # 最左边的一列的最大值是子矩阵的最大值
                    maxq.popleft()                                       # 删除最大值,因为左指针要向右移动
                if len(minq) and min(remat[l][up: down + 1]) == minq[0]:
                    minq.popleft()
                cumax, cumin = maxq[0], minq[0]        #  更新子矩阵的最大值和最小值
                l += 1                                 #  左指针要向右移动
            if cumax - cumin > lim:                    # 特判:如果l=r且最大-最小>lim
                continue    
            cu_square = (r - l + 1) * (down - up + 1)  # 计算面积
            max_res = max(max_res, cu_square)
print(max_res)

 

 

 

 

 

有关蓝桥杯刷题016——最大子矩阵(尺取法+单调队列)的更多相关文章

  1. 旋转矩阵的几何意义 - 2

    点向量坐标矩阵的几何意义介绍旋转矩阵的几何含义之前,先介绍一下点向量坐标矩阵的几何含义点:在一维空间下就是一个标量,如同一条直线上,以任意某一个位置为0点,以一定的尺度间隔为1,2,3...,相反方向为-1,-2,-3...;如此就形成了一维坐标系,这时候任何一个点都可以用一个数值表示,如点p1=5,即即从原点出发沿着x轴正方向移动5个尺度;点p2=-3,负方向移动3个尺度;     在一维坐标系上过原点做垂直于一维坐标系的直线,则形成了二维坐标系,此时描述一个点需要两个数值来表示点p3=(3,2),即从原点出发沿着x轴正方向移动3个尺度,在此基础上沿着y轴正方向移动两个尺度的位置就是点p3。

  2. ruby-on-rails - 需要帮助最大化多个相似对象中的 3 个因素并适当排序 - 2

    我需要用任何语言编写一个算法,根据3个因素对数组进行排序。我以度假村为例(如Hipmunk)。假设我想去度假。我想要最便宜的地方、最好的评论和最多的景点。但是,显然我找不到在所有3个中都排名第一的方法。Example(assumingthereare20importantattractions):ResortA:$150/night...98/100infavorablereviews...18of20attractionsResortB:$99/night...85/100infavorablereviews...12of20attractionsResortC:$120/night

  3. ruby - 获取数组中值的最大连续出现次数 - 2

    下面有没有更优雅的方法来实现这个:输入:array=[1,1,1,0,0,1,1,1,1,0]输出:4我的算法:streak=0max_streak=0arr.eachdo|n|ifn==1streak+=1elsemax_streak=streakifstreak>max_streakstreak=0endendputsmax_streak 最佳答案 类似于w0lf'sanswer,但通过从chunk返回nil来跳过元素:array.chunk{|x|x==1||nil}.map{|_,x|x.size}.max

  4. 华为OD机试真题 C++ 实现【带传送阵的矩阵游离】【2023 Q2 | 200分】 - 2

            所有题目均有五种语言实现。C实现目录、C++实现目录、Python实现目录、Java实现目录、JavaScript实现目录题目n行m列的矩阵,每个位置上有一个元素你可以上下左右行走,代价是前后两个位置元素值差的绝对值.另外,你最多可以使用一次传送阵(只能从一个数跳到另外一个相同的数)求从走上角走到右下角最少需要多少时间。输入描述:第一行两个整数n,m,分别代表矩阵的行和列。后面n行,每行m个整数,分别代表矩阵中的元素。输出描述:一个整数,表示最少需要多少时间。

  5. 蓝桥杯备赛(二) - 2

    目录前言: 一、ASC分析代码实现二、 卡片分析代码实现三、 直线分析代码实现四、货物摆放分析代码实现小结:前言:  在刷题的过程中,发现蓝桥杯的题目和力扣的差别很大。让人有一种不一样的感觉,蓝桥杯题目偏向对于实际问题用编程去的解决,而力扣给人感觉很锻炼自己的编程思维,逻辑能力。两者结合去刷,相信会有不一样的收获。 一、ASC  已知大写字母A的ASCII码为65,请问大写字母L的ASCII码是多少?分析  这道题目看上去很简单,我们需确定自己计算的准确,所以我建议用编程去解决。代码实现publicclassTest8{publicstaticvoidmain(String[]args){Sy

  6. 欧拉角表示的姿态矩阵(313和312转序) - 2

    一、习惯约定图片来自PSINS(高精度捷联惯导算法)PSINS工具箱入门与详解.pptx二、基本旋转矩阵绕x轴逆时钟旋转α\alphaα角度Rx(α)=[ 1000cos⁡αsin⁡α0−sin⁡αcos⁡α]R_x(\alpha)=\begin{bmatrix}\1&0&0\\0&\cos\alpha&\sin\alpha\\0&-\sin\alpha&\cos\alpha\end{bmatrix}Rx​(α)=​ 100​0cosα−sinα​0sinαcosα​​绕y轴逆时钟旋转α\alphaα角度Ry(α)=[ cos⁡α0−sin⁡α010sin⁡α0cos⁡α]R_y(\alpha

  7. 欧拉角、旋转矩阵及四元数 - 2

    欧拉角、旋转矩阵及四元数1.简介2.欧拉角2.1欧拉角定义2.2右手系和左手系2.3转换流程3.旋转矩阵4.四元数4.1四元数与欧拉角和旋转矩阵之间等效变换4.2测试Matlab代码5.总结1.简介常用姿态参数表达方式包括方向余弦矩阵、欧拉轴/角参数、欧拉角、四元数以及罗德里格参数等。高分辨率光学遥感卫星主要采用欧拉角与四元数对姿态参数进行描述。这里着重讲解欧拉角、旋转矩阵和四元数。2.欧拉角2.1欧拉角定义欧拉角是表征刚体旋转的一种方法之一,由莱昂哈德·欧拉引入的三个角度,用于描述刚体相对于固定坐标系的方向。在摄影测量、空间科学或其它技术领域,一般用一组(三个)欧拉角描述两个空间坐标之间的旋

  8. 蓝桥杯C/C++VIP试题每日一练之报时助手 - 2

    ?作者主页:静Yu?简介:CSDN全栈优质创作者、华为云享专家、阿里云社区博客专家,前端知识交流社区创建者?社区地址:前端知识交流社区?博主的个人博客:静Yu的个人博客?博主的个人笔记本:前端面试题个人笔记本只记录前端领域的面试题目,项目总结,面试技巧等等。接下来会更新蓝桥杯官方系统基础练习的VIP试题,依然包括解题思路,源代码等等。问题描述:给定当前的时间,请用英文的读法将它读出来。时间用时h和分m表示,在英文的读法中,读一个时间的方法是:  如果m为0,则将时读出来,然后加上“o’clock”,如3:00读作“threeo’clock”。  如果m不为0,则将时读出来,然后将分读出来,如5

  9. ruby - capybara 增加最大允许页面加载时间 - 2

    我有一个页面,有时加载时间超过一分钟。假设这是预期的行为并且不会改变。在这些情况下,我得到Net::ReadTimeout。请注意,这是在通过单击上一页上的按钮导航到页面之后,而不是ajax请求。因此Capybara.using_wait_time没有帮助。我尝试了一些激进的方法(其中一些我知道行不通),例如:设置page.driver.browser.manage.timeouts的implicit_wait、script_timeout和page_load。遍历整个对象空间并设置所有Selenium::WebDriver::Remote::Http::Default的timeout

  10. Ruby - 找到哈希最大值的键 - 2

    我有一个散列,我想返回散列最大值的键(或键/值对)。所以,如果只有一个真正的最大值,它将返回那个键;但是,如果有多个具有相同值的键/值对,它将返回所有这些键。我如何在Ruby中完成此操作?my_hash.max_by{|k,v|v}#onlyreturnsonekey/valuepair 最佳答案 如果你想要所有对,我会做类似的事情max=my_hash.values.maxHash[my_hash.select{|k,v|v==max}] 关于Ruby-找到哈希最大值的键,我们在Sta

随机推荐