栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

1091 理想的正方形(单调队列优化)

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

1091 理想的正方形(单调队列优化)

1. 问题描述:

有一个 a×b 的整数组成的矩阵,现请你从中找出一个 n×n 的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

输入格式

第一行为三个整数,分别表示 a,b,n 的值;第二行至第 a+1 行每行为 b 个非负整数,表示矩阵中相应位置上的数。

输出格式

输出仅一个整数,为 a×b 矩阵中所有“n×n 正方形区域中的最大整数和最小整数的差值”的最小值。

数据范围

2 ≤ a,b ≤ 1000,
n ≤ a,n ≤ b,n ≤ 100,
矩阵中的所有数都不超过 10 ^ 9。

输入样例:

5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2

输出样例:

1
来源:https://www.acwing.com/problem/content/description/1093/

2. 思路分析:

这道题目是在二维平面内求解长度为k的正方形的最值,最容易想到的方法是求解每一行所有长度为k的区间最值,然后在列的方向上再求解一遍最值得到的就是以当前列为右端点的长度为k的区间最值,而在行方向上或者列方向上求解最值实际上是一维平面内滑动窗口求最值的问题,可以使用单调队列优化,我们可以求解出每一行与每一列的最值之后保存到对应的两个数组上即可(row_min保存行最小值,row_max保存行最大值),每一次求解以当前列的位置作为右端点的长度为k的正方形的最值,枚举每一列对应的所有正方形的最值即可得到所有长度为k的正方形的最值,在枚举的时候更新答案即可。

3. 代码如下:

from typing import List


class Solution:
    # 单调队列的过程, 求解每一行长度为k的最小值(求解最大值的时候也是类似的, 只需要修改一下判断的符号即可)
    def getMin(self, a: List[int], b: List[int], maxLen: int, k: int):
        q = [0] * (maxLen + 1)
        hh, tt = 0, -1
        for i in range(1, len(a)):
            # 因为一开始初始化的时候tt = -1所以这里需要判断一下hh是否小于等于tt
            if hh <= tt and q[hh] <= i - k: hh += 1
            while hh <= tt and a[i] <= a[q[tt]]: tt -= 1
            tt += 1
            q[tt] = i
            # 以当前位置i为右端点的长度为k的区间长度最小值为a[q[hh]]
            b[i] = a[q[hh]]
    
    # 单调队列的过程, 求解每一行长度为k的最大值
    def getMax(self, a: List[int], b: List[int], maxLen: int, k: int):
        q = [0] * (maxLen + 1)
        hh, tt = 0, -1
        for i in range(1, len(a)):
            if hh <= tt and q[hh] <= i - k: hh += 1
            while hh <= tt and a[i] >= a[q[tt]]: tt -= 1
            tt += 1
            q[tt] = i
            b[i] = a[q[hh]]

    def process(self):
        n, m, k = map(int, input().split())
        # 求解矩阵边长的最大值(因为后面是有单调队列求解最值的时候每一行和每一列都需要求解最值而且都是调用同一个方法所以需要先求解出矩阵最大的边长)
        maxLen = max(n, m)
        # 先声明第一行全为0这样下标可以从1开始
        w = [[0] * (m + 1)]
        for i in range(n):
            # 每一行前面加上一个0这样下标可以从1开始
            w.append([0] + list(map(int, input().split())))
        # 记录行的最小值和最大值
        row_min, row_max = [[0] * (m + 1) for i in range(n + 1)], [[0] * (m + 1) for i in range(n + 1)]
        for i in range(1, n + 1):
            self.getMin(w[i], row_min[i], maxLen, k)
            self.getMax(w[i], row_max[i], maxLen, k)
        # a, b, c为三个辅助列表, 每一次都是以当前列的所在长度为k的正方形的最大值与最小值的差值
        a, b, c = [0] * (maxLen + 1), [0] * (maxLen + 1), [0] * (maxLen + 1)
        res = 10 ** 9
        for i in range(k, m + 1):
            # 先将当前这一列的所有最小值先保存到a数组中
            for j in range(1, n + 1):
                a[j] = row_min[j][i]
            # 将求解的列最小值结果保存到b数组中
            self.getMin(a, b, maxLen, k)
            for j in range(1, n + 1):
                a[j] = row_max[j][i]
            # 将求解的列最大值结果保存到c数组中
            self.getMax(a, c, maxLen, k)
            # 枚举以当前所以列为右端点长度为k的正方形, 注意起点应该是k这样才能够构成长度为k的正方形
            for j in range(k, n + 1): res = min(res, c[j] - b[j])
        return res


if __name__ == "__main__":
    print(Solution().process())
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/302486.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号