(第十三届蓝桥杯省赛)F:统计子矩阵(双指针)

样例输入

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

样例输出

19

分析:赛前没见过这样的做题方法,赛后才知道原来具有某种单调性质的子矩阵问题还可以通过双指针来进行优化,下面来对这种方法进行一下介绍:

我们先枚举矩阵的左右边界,这个复杂度是n^2的,如图所示:

确定L和R后,我们来枚举上下边界,在上下左右边界内的数都是我们所需要选择的数,因为我们能保证枚举完所有的可能成为答案的上下左右区间,所以这样枚举也是正确的。那是不是枚举上下边界也是n^2的复杂度呢?其实不是,题目中说了方格中的每一个数都是正数,也就是说在原来面积的基础上扩大面积,则和是单调不降的,换句话说在原来的基础上把下边界下移不会导致我们所选择的数的和变小,只会变大或者保持不变。所以我们就可以利用这个单调性来进行优化,由于左右边界都已经确定了,我们先预处理出来每一行第i列到第j列的元素和,这个可以通过前缀和相减得到,记录s[i][j]表示第i行前j列元素的和,也就是a[i][1]+……+a[i][j],那么我们在枚举L~R之间的元素时就可以o(1)处理出来任意一行第L列到第R列的数,那么就相当于在枚举一个个数,第一个数为s[1][L~R](s[R]-s[L-1]),第二个数是s[2][L~R],第i个数是s[i][L~R],那么就相当于我们有n个数(共有n行),然后求有多少种情况满足连续的若干个数的和小于等于K,这个显然可以用双指针来做,也就是先固定上边界,向下移动下边界,直到所选区域内的数的和大于k为止,不妨设这个时候的上边界为u,下边界为d,那么只要下边界位于u~d-1之间都是满足区域和小于等于k的(等价于下边界上移),也就是说以L和R作为左右边界且以u做为上边界的可能下边界有d-u种情况,我们只需要把这个记录到答案内即可。需要注意的是下边界不能超过n,当下边界无法移动时,这个时候再移动上边界,直到上边界也移动到最下方为止,这就是一个基本的双指针求连续区间和的问题。这一部分复杂度为o(n),所以这道题目总的复杂度就是o(n^3)。

这道题给我们的启发就是对于答案具有某种单调性的子矩阵问题(例如随着选取面积的增大而增大或者减少的某种性质),或许我们并不需要利用二维前缀和进行o(n^4)进行枚举,我们可以通过先枚举左右边界,然后利用单调性来进行双指针枚举上下边界求取满足某种性质的方案数,思想还是挺巧妙的。

下面是代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(fast)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<set>
#include<vector>
#include<map>
#include<queue>
using namespace std;
const int N=503;
long long s[N][N];//s[i][j]表示第i行前j列的和,也就是a[i][1]+……+a[i][j] 
int main()
{
	int n,m,K;
	cin>>n>>m>>K;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		scanf("%lld",&s[i][j]);
		s[i][j]+=s[i][j-1];
	}
	long long ans=0;
	for(int l=1;l<=m;l++)
	for(int r=l;r<=m;r++)
	{
		int d=1;
		long long sum=0;
		for(int u=1;u<=n;u++)
		{
			while(sum+s[d][r]-s[d][l-1]<=K&&d<=n)
			{
				sum+=s[d][r]-s[d][l-1];
				d++;
			}
			sum-=s[u][r]-s[u][l-1];
			ans+=d-u;//在u到d-1这段区间内的和都是小于等于K的
		}
	}
	printf("%lld",ans);
	return 0;
}

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
蓝桥杯统计矩阵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问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值