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

链接:这里

AC CODE

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#define inf 0x3f3f3f3f
#define ll long long

using namespace std;

int n, k, m;
ll ans;
int maxm = -inf;
int a[505][505];

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]);
			a[i][j] += a[i - 1][j];
		}
	}
	
	
	for(int i = 1; i <= n; ++i){
		for(int j = i; j <= n; ++j){
			for(int l = 1, r = 1, sum = 0; r <= m; ++r){
				sum += a[j][r] - a[i - 1][r];
				while(sum > k){
					sum -= a[j][l] - a[i - 1][l];
					l++;
				}
				ans += r - l + 1;
			}
		}
	}
	
	cout << ans << endl;
	
	return 0;
}

解题报告


题意解析

本题需要我们在一个数字矩阵里找出总和不大于k 的子矩阵数目


思路

描述一个矩阵需要找到矩阵四个角的位置,我们直接用暴力枚举(某一点的xy和长、宽;也可以理解为上下左右四个边界),时间复杂度是o(n4),接着把矩阵里面的数据全部加起来,比较计数。数据最大为500,显然会TLE。此法可过30%数据

30% CODE


如果用二维前缀和优化矩阵,可以使查询时间变为o(1),但是依然要枚举四次。此法可过70%数据

70% CODE


那有没有办法优化掉枚举呢?当然:
我们依然枚举上下边界,然后用一维前缀和处理单列的矩阵,这样一个二维的数据统计就变为了一维,接着用双指针处理一维的序列(即处理下边界的一维数组),先让右指针r一直走到最右位(条件是矩阵数字和小于等于k,那么最右位应该是合法矩阵的右边界 + 1),走的每一步如果合法,答案ans加上最大矩阵的各子矩阵数目总和(一个集合有多少子集合问题,拎出来一个点然后分别与其他点进行匹配,有r - l个,由于本身也算子集所以 + 1即为r - l + 1);不合法就移动左指针使之合法。
此方法AC(抄的y总思路,他太强了,我是菜逼)。

AC CODE

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#define inf 0x3f3f3f3f
#define ll long long

using namespace std;

int n, k, m;
ll ans;
int maxm = -inf;
int a[505][505];

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]);
			a[i][j] += a[i - 1][j];
		}
	}
	
	
	for(int i = 1; i <= n; ++i){
		for(int j = i; j <= n; ++j){
			for(int l = 1, r = 1, sum = 0; r <= m; ++r){
				sum += a[j][r] - a[i - 1][r];
				while(sum > k){
					sum -= a[j][l] - a[i - 1][l];
					l++;
				}
				ans += r - l + 1;
			}
		}
	}
	
	cout << ans << endl;
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值