蓝桥真题:统计子矩阵(c++题解 + 分析 前缀和+双指针)

本文介绍了一种使用列前缀和数组的方法,通过四个指针(top,bot,l,r)遍历并计算满足特定条件(和小于K)的子矩阵个数。核心逻辑是维护一个sum变量,当sum超过K时调整左边界l,直到sum小于K。给出的C++代码实现了这一过程。
摘要由CSDN通过智能技术生成

问题概述:

输入:

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

输出: 

19

 

题目分析:

我们可以将数组存为一个前缀和数组,不同的是这是列的前缀和

如:


        1  2  3 4                                                          1  2  3  4
        5  6  7 8            存为列的前缀和数组为:      6  8  10 12
        9 10 11 12                                                      15 18  21 24
    

(1)我们可以用两个指针 

top:用来遍历每一行 范围:1<=top<=N

bot:用来遍历从top开始的每一行 范围 :top<=bot<=N

每次就把矩阵截取出了一个 (bot-top+1)*M 的子矩阵

如:

  1  2  3  4

  6  8 10 12         这个矩阵在top=1&&bot = 2时为            6  8  10  12      这个子矩阵

15 18 21 24                                                                       15 18 21 24

这样就可以遍历从top开始的每一个 (bot-top+1)*M 维度的子矩阵

(2)接着可以再定义两个指针 

        l:表示子矩阵的左边界 范围:1<=l<=M

        r:表示子矩阵的右边界 范围 : 1<=r<=M

通过l与r的变换 再结合之前 top 与 bot 这四个指针 ,分别定义了矩阵的左边界,右边界,上边界与下边界。

如:top =1 ;        bot =2 ;        l =1;        r=2;

此时截取出来的矩阵为:

        8   10

       18   21

这样通过这些变换,我们就可以遍历或得所有的子矩阵:

//遍历所有的子矩阵
for (int top = 1; top<=N; top++) {
		for (int bot = top; bot <= N; bot++) {
			l = r = 1;
			
			for (int r = 1; r <=M; r++) {
				//核心逻辑实现
			}

		}
	}

(3)核心功能实现

我们可以用一个sum来保存当前以 [bot,r] 为右下角的子矩阵的和

我们可以推出[bot,r] 为右下角的子矩阵的和:

                             sum += arr[bot][r] - arr[top-1][r];

如果sum大于K 则让 l 向右移动 缩小矩阵宽度 ,移动至sum<=K为止

此时公式为:        sum -= (arr[bot][l]-arr[top-1][l]);
                              l++;

此时 r-l+1 便所有以 [bot,r] 为右下角的满足条件的子矩阵

如:top=2         bot =2        l =1        r=2 时  此时sum = 11 大于 K(K =10)

   1  2  3 4                                                            1  2  3  4
   5  6  7 8                                   arr数组:           6  8  10 12
   9 10 11 12                                                      15 18  21 24

    

则此时   sum =sum - (arr[2][1]-arr[1][1])=11-6-1 = 6; l++ 

此时sum<K 满足条件 l便可以停止右移

 整体代码实现:


#include <iostream>
using namespace std;
long long N, M, K;
int arr[507][507];//列前缀和数组
int main()
{
	cin >> N >> M >> K;
	for (int i = 1; i <=N; i++) {
		for (int j = 1; j <= M; j++) {
			cin >> arr[i][j];
			arr[i][j] += arr[i - 1][j];
		}
	}

	/*
		1  2  3 4        1  2  3  4
		5  6  7 8     -> 6  8  10 12
		9 10 11 12      15 18  21 24
	*/
	int l, r;
	long long sum = 0;
	long long ans = 0;
	for (int top = 1; top<=N; top++) {
		for (int bot = top; bot <= N; bot++) {
			l = r = 1;
			sum = 0;
			for (int r = 1; r <=M; r++) {
				sum += arr[bot][r] - arr[top-1][r];
				while (sum > K) {
					sum -= (arr[bot][l]-arr[top-1][l]);
					l++;
				}
				ans += r - l + 1;
			}

		}
	}
	cout << ans << endl;


	return 0;
}

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值