【前缀和+滑动窗口问题】2022年蓝桥杯真题之统计子矩阵

⭐️前面的话⭐️

本篇文章介绍来自2022年蓝桥杯真题之统计子矩阵,算法考点为前缀和和滑动窗口,展示语言java。

📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
📆首发时间:🌴2023年3月28日🌴
✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《算法》,📚《算法导论》
💬参考在线编程网站:🌐牛客网🌐力扣
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!



在这里插入图片描述


⭐️统计子矩阵⭐️

🔐题目详情

统计子矩阵
问题描述
给定一个 N × M N \times M N×M 的矩阵 A A A, 请你统计有多少个子矩阵 (最小 1 × 1 1 \times 1 1×1, 最大 N × M ) N \times M) N×M)满足子矩阵中所有数的和不超过给定的整数 K K K ?
输入格式
之后 N N N 行每行包含 M M M 个踝数, 代表知阵 A A A.
输出格式
一个㮛数代表暜案。
样例输入

3 4 10
1 2 3 4
5 6 7 8
9 10 11 12

样例输出

19

样例说明
满足条件的子知阵一共有 19 , 包含:
大小为 1 × 1 1 \times 1 1×1 的有 10 个。
大小为 1 × 2 1 \times 2 1×2 的有 3 个。
大小为 1 × 3 1 \times 3 1×3 的有 2 个。
大小为 1 × 4 1 \times 4 1×4 的有 1 个。
大小为 2 × 1 2 \times 1 2×1 的有 3 个。
评测用例规模与约定
对于 30 % 30 \% 30% 的敞据, N , M ≤ 20 N, M \leq 20 N,M20.
对于 70 % 70 \% 70% 的数据, N , M ≤ 100 N, M \leq 100 N,M100.
对于 100 % 100 \% 100% 的数据, 1 ≤ N , M ≤ 500 ; 0 ≤ A i j ≤ 1000 ; 1 ≤ K ≤ 250000000 1 \leq N, M \leq 500 ; 0 \leq A_{i j} \leq 1000 ; 1 \leq K \leq 250000000 1N,M500;0Aij1000;1K250000000.
运行限制

  • 最大运行时间: 1 s
  • 最大运行内存: 256 M 256 \mathrm{M} 256M

💡解题思路

思路1:二维前缀和,不知道咋高效率枚举两个点,超时了,预计能过70%。
思路2:求每一列的前缀和,再枚举矩阵纵向长的长度,使用滑动窗口枚举横向长度,注意答案爆了 i n t int int

🔑源代码

70分:二维前缀和

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
//二维前缀和
// s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j]
const int N = 520;
LL s[N][N], a[N][N];
int n, m;
LL k;

int main() 
{
  cin >> n >> m >> k;

  for (int i = 1; i <= n; i++) 
  {
    for (int j = 1; j <= m; j++) 
    {
      cin >> a[i][j];
      s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
    }
  }
  int ans = 0;
  //枚举两个点
  for (int li = 1; li <= n; li++) 
  {
    for (int lj = 1; lj <= m; lj++) 
    {
      for (int ri = li; ri <= n; ri++) 
      {
        for (int rj = lj; rj <= m; rj++) 
        {
          LL cur = s[ri][rj] - s[li - 1][rj] - s[ri][lj - 1] + s[li - 1][lj - 1];
          if (cur <= k) ans++;
          else break;
        }
      }
    }
  }
  cout << ans << endl;
  return 0;
}

满分做法:按列求前缀和+滑动窗口

c++

#include <iostream>
#include <algorithm>

using namespace std;
const int N = 520;
typedef long long LL;
//求每一列的前缀和,枚举矩阵上限长度,最后使用滑动窗口求(100分)
LL s[N][N];
int n, m;
LL k;

int main() 
{
  cin >> n >> m >> k;

  for (int i = 1; i <= n; i++) 
  {
    for (int j = 1; j <= m; j++) 
    {
      int a;
      cin >> a;
      s[i][j] = s[i - 1][j] + a;
    }
  }
  LL ans = 0;
  //枚举竖边
  for (int i = 1; i <= n; i++) 
  {
    for (int j = i; j <= n; j++) 
    {
      //在竖边从左到右滑动窗口
      int l = 1, r = 1;
      LL sum = 0;
      while (r <= m) 
      {
        //累加当前窗口的和,即在第r列,第i-j行之间的和
        sum += s[j][r] - s[i - 1][r];

        //如果超出k,将左窗口缩小
        while (sum > k) 
        {
          //取消左端点的贡献
          sum -= s[j][l] - s[i - 1][l];
          l++;
        }
        ans += r - l + 1;
        r++;
      }
    }
  }
  cout << ans << endl;
  return 0;
}

满分做法:java版本

import java.util.*;
import java.io.*;

public class Main 
{
  static final int N = 520;
  static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  static final PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));

  static long[][] s = new long[N][N];
  static int n, m;
  static long k;

  public static void main(String[] args) throws IOException {
    String[] ss = br.readLine().split(" ");
    n = Integer.parseInt(ss[0]);
    m = Integer.parseInt(ss[1]);
    k = Long.parseLong(ss[2]);

    for (int i = 1; i <= n; i++) {
      ss = br.readLine().split(" ");
      for (int j = 1; j <= m; j++) {
        long a = Long.parseLong(ss[j - 1]);
        s[i][j] = s[i - 1][j] + a;
      }
    }

    //枚举纵向边长
    long ans = 0;
    for (int i = 1; i <= n; i++) {
      for (int j = i; j <= n; j++) {
        //滑动窗口
        int l = 1;
        int r = 1;
        long sum = 0;
        while (r <= m) {
          sum += s[j][r] - s[i - 1][r];
          while (sum > k) {
            sum -= s[j][l] - s[i - 1][l];
            l++;
          }
          ans += r - l + 1;
          r++;
        }
      }
    }
    out.println(ans);
    out.close();
  }
}

🌱总结

前缀和+滑动窗口


觉得文章写得不错的老铁们,点赞评论关注走一波!谢谢啦!

1-99

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
蓝桥杯统计矩阵c问题是一个经典的算法问题,可以通过编程实现。下面我将用300字中文回答如何实现。 我们可以使用二维前缀和的方法来解决这个问题。首先,我们定义一个二维数组prefixSum,用来存储原始矩阵a中每个位置(i,j)之前所有元素的和。 我们可以通过以下方式计算prefixSum: 1. 遍历矩阵a的每个位置(i,j),计算prefixSum[i][j]: - 如果i=0且j=0,则prefixSum[i][j] = a[i][j]; - 如果i=0且j≠0,则prefixSum[i][j] = prefixSum[i][j-1] + a[i][j]; - 如果i≠0且j=0,则prefixSum[i][j] = prefixSum[i-1][j] + a[i][j]; - 如果i≠0且j≠0,则prefixSum[i][j] = prefixSum[i-1][j] + prefixSum[i][j-1] - prefixSum[i-1][j-1] + a[i][j]。 2. 接下来,我们可以通过计算prefixSum来统计矩阵c的和。对于每个矩阵c,我们可以使用以下方式计算其和: - 定义矩阵c的左上角位置为(i1, j1),右下角位置为(i2, j2); - 如果(i1, j1)为原始矩阵a的左上角,则c的和为prefixSum[i2][j2]; - 如果(i1, j1)为原始矩阵a的第一行,则c的和为prefixSum[i2][j2] - prefixSum[i2][j1-1]; - 如果(i1, j1)为原始矩阵a的第一列,则c的和为prefixSum[i2][j2] - prefixSum[i1-1][j2]; - 其他情况下,c的和为prefixSum[i2][j2] - prefixSum[i1-1][j2] - prefixSum[i2][j1-1] + prefixSum[i1-1][j1-1]。 通过以上的方法,我们可以编写代码来实现这个算法。代码的时间复杂度为O(M*N),其中M和N分别为原始矩阵a的行数和列数。因此,我们可以通过这个方法高效地解决蓝桥杯统计矩阵c问题

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

未见花闻

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

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

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

打赏作者

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

抵扣说明:

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

余额充值