洛谷P8783 [蓝桥杯 2022 省 B] 统计子矩阵

题目描述

给定一个 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≤2.5×108.

蓝桥杯 2022 省赛 B 组 F 题。
P8783 [蓝桥杯 2022 省 B] 统计子矩阵 - 洛谷


这题我运用二维前缀和公式和然后利用计算子矩阵和公式解决这题,但只得了80分


使用二维前缀和,用 sum[i][j] 表示矩阵中 x∈[1,i],y∈[1,j] 的所有元素之和。其递推式为 sum[i][j]=sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1]+arr[i][j]

所以矩阵该矩阵的元素之和就可以用 sum[c][d] - sum[c][b - 1] - sum[a - 1][d] + sum[a - 1][b - 1]

 以下是我c语言提交的代码仅供参考:
 

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<limits.h>
#include<stdlib.h>
#include<math.h>
#include <stdbool.h>

int sum[501][501] = { 0 };

//计算二维矩阵中从左上角坐标 (a,b) 到右下角坐标 (c,d) 的子矩阵的和。
int sums(int a, int b, int c, int d)
{
    return sum[c][d] - sum[c][b - 1] - sum[a - 1][d] + sum[a - 1][b - 1];
}
int main() 
{
    int n, m, k = 0; int count = 0;
    int arr[501][501] = { 0 };

    scanf("%d %d %d", &n, &m,&k);

    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            scanf("%d", &arr[i][j]);
        }
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            //二维前缀和计算
            sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + arr[i][j];
        }
    }

    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            for (int x = i; x <= n; x++)
            {
                for (int y = j; y <= m; y++)
                {
                    if (sums(i, j, x, y) <= k)
                    {
                        count++;
                    }
                }

            }
        }
    }

    printf("%d\n", count);
    return 0;
}

 

### 蓝桥杯统计子矩阵算法与解题思路 #### 1. 问题概述 蓝桥杯中的“统计子矩阵”问题是经典的动态规划和优化问题之一。题目要求在一个 \(N \times M\) 的矩阵中找到所有可能的子矩阵,这些子矩阵的元素总和不超过给定阈值 \(K\)。 #### 2. 解题方法分析 ##### 方法一:二维前缀和(超时) 通过构建二维前缀和数来快速计算任意子矩阵的和。设 `prefix[i][j]` 表示从左上角 `(0, 0)` 到右下角 `(i, j)` 子矩阵的元素和,则可以通过如下公式计算: \[ \text{sum}(x_1, y_1, x_2, y_2) = prefix[x_2][y_2] - prefix[x_1-1][y_2] - prefix[x_2][y_1-1] + prefix[x_1-1][y_1-1] \] 这种方法的时间复杂度为 \(O(N^2M^2)\),对于较大的数据规模会超出时间限制[^1]。 ##### 方法二:二维前缀和 + 双指针(AC) 为了降低时间复杂度,可以采用压缩维度的思想。具体步骤如下: 1. **固定上下边界**:枚举两个行号作为子矩阵的上下边界。 2. **按列累加**:将选定范围内的每一列视为一个新的序列,利用一维前缀和加速求和过程。 3. **双指针优化**:在一维序列中应用滑动窗口技术,寻找符合条件的最大连续子段。 此方法的核心在于减少不必要的重复计算,其时间复杂度降为 \(O(N^2M)\)[^2]。 ```python def count_submatrices(matrix, k): n, m = len(matrix), len(matrix[0]) result = 0 for top in range(n): # 枚举上边界 row_sum = [0] * m for bottom in range(top, n): # 枚举下边界 for col in range(m): # 累加当前范围内每列的和 row_sum[col] += matrix[bottom][col] # 使用双指针查找满足条件的子矩阵 current_sum = 0 start = 0 for end in range(m): current_sum += row_sum[end] while start <= end and current_sum > k: current_sum -= row_sum[start] start += 1 if current_sum <= k: result += (end - start + 1) return result ``` 上述代码实现了基于二维前缀和与双指针的方法,能够高效解决该问题[^3]。 #### 3. 单调队列的应用 另一种更高级的技术是使用单调队列处理区间最值问题。首先针对每一行分别预处理出所有区间的最大/最小值,再扩展到整个矩阵的高度方向。最终得到的结果即为所有合法子矩阵的数量[^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值