蓝桥杯-子矩阵

文章讲述了如何使用二维单调队列算法求解矩阵中子区域的区间最大值和最小值,通过先按行求每行的区间最值,再对这些行进行纵向处理,最后计算并返回所有子矩阵区间的乘积和模998244353的结果。
摘要由CSDN通过智能技术生成
"""
题目来源
https://www.lanqiao.cn/problems/3521/learning/?page=1&first_category_id=1&name=%E5%AD%90%E7%9F%A9%E9%98%B5
"""
import os
import sys
from collections import deque

# 请在此输入您的代码
n, m, a, b = map(int, input().split())
datas = []
for i in range(n):
  datas.append(list(map(int, input().split())))

# 记录所有子矩阵区间最大值
def max_value_queue(x):
  # 记录每一行所有区间长度为b的区间最大值
  ws = []
  for i in range(n):
    nums = datas[i]
    # w记录当前行所有区间长度为b的区间最大值
    w = []
    # 单调队列
    q = deque()
    for i, v in enumerate(nums):
      # 1.入队列
      while q and v >= nums[q[-1]]:
          q.pop()
      q.append(i)
      # 2.出队列
      # 单调队列的长度限制为b, 当队列元素个数大于b就需要将队首元素出队
      if i - q[0] + 1 >= b + 1:
          q.popleft()
      # 3.开始记录答案
      if i >= b - 1:
          # 单调队列降序, 队首元素为区间最大值
          w.append(nums[q[0]])
    ws.append(w)

  # 记录所有二维区间长度为a宽度为b的区间最大值
  ans = []
  
  # 将ws中的每一列作为一行
  tmps = [[ws[j][i] for j in range(n)] for i in range(len(ws[0]))]
  for i in range(len(ws[0])):
    nums = tmps[i]
    # w记录当前行所有区间长度为a的区间最大值
    w = []
    # 单调队列
    q = deque()
    for i, v in enumerate(nums):
      # 1.入队列
      while q and v >= nums[q[-1]]:
          q.pop()
      q.append(i)
      # 2.出队列
      # 单调队列的长度限制为a, 当队列元素个数大于a就需要将队首元素出队
      if i - q[0] + 1 >= a + 1:
          q.popleft()
      # 3.开始记录答案
      if i >= a - 1:
          # 单调队列降序, 队首元素为区间最大值
          w.append(nums[q[0]])
    ans.append(w)
  return ans

# 记录所有矩阵最小值
def min_value_queue(x):
  # 记录每一行所有区间长度为b的区间最小值
  ws = []
  for i in range(n):
    nums = datas[i]
    # w记录当前行所有区间长度为b的区间最小值
    w = []
    # 单调队列
    q = deque()
    for i, v in enumerate(nums):
      # 1.入队列
      while q and v <= nums[q[-1]]:
          q.pop()
      q.append(i)
      # 2.出队列
      # 单调队列的长度限制为b, 当队列元素个数大于b就需要将队首元素出队
      if i - q[0] + 1 >= b + 1:
          q.popleft()
      # 3.开始记录答案
      if i >= b - 1:
          # 单调队列降序, 队首元素为区间最小值
          w.append(nums[q[0]])
    ws.append(w)

  # 记录所有二维区间长度为a宽度为b的区间最小值
  ans = []
  
  # 将ws中的每一列作为一行
  tmps = [[ws[j][i] for j in range(n)] for i in range(len(ws[0]))]
  for i in range(len(ws[0])):
    nums = tmps[i]
    # w记录当前行所有区间长度为a的区间最小值
    w = []
    # 单调队列
    q = deque()
    for i, v in enumerate(nums):
      # 1.入队列
      while q and v <= nums[q[-1]]:
          q.pop()
      q.append(i)
      # 2.出队列
      # 单调队列的长度限制为a, 当队列元素个数大于a就需要将队首元素出队
      if i - q[0] + 1 >= a + 1:
          q.popleft()
      # 3.开始记录答案
      if i >= a - 1:
          # 单调队列降序, 队首元素为区间最小值
          w.append(nums[q[0]])
    ans.append(w)
  return ans
maxs = max_value_queue(datas)
mins = min_value_queue(datas)

answer = 0
for i in range(len(maxs)):
  for j in range(len(maxs[0])):
    answer = (answer + maxs[i][j] * mins[i][j]) % 998244353
print(answer)

解题思路:

这个就是二维单调队列的使用, 一维单调队列求区间最值比较简单, 可以去做一下滑动窗口最大值:

https://leetcode.cn/problems/sliding-window-maximum/, 那么二维区间最值怎么求呢?

首先从横向来看,可以用单调队列将每一行的区间最值求出来, 用数组w记录当前行的所有区间最值, 用ws将每一行的w都存储一起。接下来再来纵看,取已经求得的ws二维数组其中一列,求纵向的区间最值, 用t记录当前列的区间最值, 用ts记录每一列的t。最后ts就存储了所有的子矩阵的区间最值。

将区间最大值和区间最小值相乘求和求模就是最终答案, 代码看起来很长, 其实功能比较相似, 两个函数都是求区间最值, 一个是最大一个是最小。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好无聊啊,烦死

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值