蓝桥 2109统计子矩阵

问题描述

给定一个N×M 的矩阵 A, 请你统计有多少个子矩阵 (最小 1×1, 最大 N×M) 满足子矩阵中所有数的和不超过给定的整数 K ?

输入格式

第一行包含三个整数 N,M 和 K.

之后 N 行每行包含 M 个整数, 代表矩阵 A.

输出格式

一个整数代表答案。

样例输入

复制代码
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12

样例输出

复制代码
19

样例说明

满足条件的子矩阵一共有 19 , 包含:

大小为 1×1 的有 10 个。

大小为 1×2 的有 3 个。

大小为 1×3 的有 2 个。

大小为 1×4 的有 1 个。

大小为 2×1 的有 3 个。

评测用例规模与约定

对于 30% 的数据, N,M≤20.

对于 70% 的数据, N,M≤100.

对于 100% 的数据, 1≤N,M≤500; 0≤Aij≤1000; 1≤K≤250000000

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

方法1:暴力(70分)

枚举每个点为起点,枚举每个点为终点,这样就可以得到所有的子矩阵,但时间复杂度是O(n^4),会超时

cpp 复制代码
#include <iostream>
using namespace std;

int n, m, k;
int a[510][510];
long long ans;

int main() 
{
    cin >> n >> m >> k;
    for (int i = 1; i <= n; ++i) 
	{
        for (int j = 1; j <= m; ++j) 
		{
            cin >> a[i][j];
            //二维前缀和,第i行j列格子左上部分所有元素的和
            a[i][j] = a[i][j] + a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
        }
    }
    
    //外层两重循环 (i 和 j) 遍历所有可能的左上角位置
    for (int i = 1; i <= n; ++i) 
	{
        for (int j = 1; j <= m; ++j) 
		{
			//内层两重循环 (l 和 p) 遍历所有可能的右下角位置
			//l >= i 且 p >= j
            for (int l = i; l <= n; ++l) 
			{
                for (int p = j; p <= m; ++p) 
				{
                    //以(i, j)为左上角,(l, p)为右下角的子矩阵的和为:
                    int sum = a[l][p] - a[l][j - 1] - a[i - 1][p] + a[i - 1][j - 1];
                    if (sum <= k) ans++;
                }
            }
        }
    }
    cout << ans;
    return 0;
}

方法2:前缀和 二维滑动窗口

i,j分别表示一个区域的左右边界,p,q来表示上下两个指针,p一开始在最上面,不断向下来找q,直到权值和小于等于k(而且不能越界也就是p>q),然后p q这片区域的行数q-p+1就是子矩阵的个数

cpp 复制代码
#include<iostream>
using namespace std;

int n, m, k;
int a[510][510];
long long ans;

int main() 
{
    cin >> n >> m >> k;
    for (int i = 1; i <= n; ++i) 
	{
        for (int j = 1; j <= m; ++j) 
		{
            scanf("%d", &a[i][j]);
            
            //二维前缀和,第i行j列格子左上部分所有元素的和
            a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
        }
    }
    
    for (int i = 1; i <= m; ++i) 
	{
        for (int j = i; j <= m; ++j) 
		{
            for (int p = 1, q = 1; q <= n; ++q) 
			{
				//以(p, i)为左上角,(q, j)为右下角的子矩阵的和>k
                while (p <= q && a[q][j] - a[q][i - 1] - a[p - 1][j] + a[p - 1][i - 1] > k) 
				{
                    //一直让p指针下移到权值和<=k
                    p++;
                }
                if (p <= q) 
				{
                    //在当前i,j的情况下,此时p q 之间的所有子矩阵都满足条件,一共q-p+1行
                    ans += (q - p + 1);
                }
            }
        }
    }
    cout << ans;
    
    return 0;
}
相关推荐
qystca1 天前
小秋的矩阵
前缀和·矩阵·二维前缀和
自信的小螺丝钉11 天前
Leetcode 1477. 找两个和为目标值且不重叠的子数组 前缀和+DP
算法·leetcode·前缀和·动态规划
闻缺陷则喜何志丹13 天前
【二分查找 前缀和】P10429 [蓝桥杯 2024 省 B] 拔河|普及+
c++·算法·前缀和·蓝桥杯·二分查找·洛谷·拔河
闻缺陷则喜何志丹1 个月前
【C++前缀和 排序】891. 子序列宽度之和|2182
c++·算法·前缀和·力扣·排序·子序列·宽度
某个默默无闻奋斗的人1 个月前
二维前缀和:高效求解矩阵区域和问题
java·算法·leetcode·前缀和
不想当程序猿_3 个月前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
熬夜学编程的小王3 个月前
【优选算法篇】前缀和与哈希表的完美结合:掌握子数组问题的关键(下篇)
数据结构·c++·算法·前缀和·蓝桥杯
银氨溶液3 个月前
力扣.——560. 和为 K 的子数组
数据结构·c++·算法·leetcode·前缀和·职场和发展
南桥几晴秋3 个月前
【算法刷题指南】前缀和
c++·算法·前缀和