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

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

题目描述

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

输入格式

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

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

输出格式

一个整数代表答案。

样例 #1

样例输入 #1

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

样例输出 #1

19

提示

【样例说明】

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

大小为 1 × 1 1 \times 1 1×1 的有 10 10 10 个。

大小为 1 × 2 1 \times 2 1×2 的有 3 3 3 个。 大小为 1 × 3 1 \times 3 1×3 的有 2 2 2 个。

大小为 1 × 4 1 \times 4 1×4 的有 1 1 1 个。

大小为 2 × 1 2 \times 1 2×1 的有 3 3 3 个。

【评测用例规模与约定】

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

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

对于 100 % 100 \% 100% 的数据, 1 ≤ N , M ≤ 500 , 0 ≤ A i j ≤ 1000 , 1 ≤ K ≤ 2.5 × 1 0 8 1 \leq N, M \leq 500,0 \leq A_{i j} \leq 1000,1 \leq K \leq 2.5\times10^8 1N,M500,0Aij1000,1K2.5×108.

蓝桥杯 2022 省赛 B 组 F 题。

思路

【暴力做法】

枚举四个自由度,然后用二维前缀和
求出满足<=k的所有子矩阵
但是N<=500
n^4显然会超时

【优化】

去除枚举的一个自由度
枚举上下边界后
只考虑一维的左右边界
当R确定后,L往左走,则区间和一定是单调的
此时可以利用二分和双指针
二分则时间复杂度度为n^3(不太可取)
双指针时间复杂度为n^2

在这里插入图片描述
【双指针】

首先预处理矩阵为一维前缀和
将L定义为R的函数
L随着R的变化而单调变化
定义:L为R确定后,能取到的最左边的值
那么R往右走,L一定向右走

【二分】

首先预处理矩阵为二维前缀和
确定上下边界后
将第三重循环定义为右边界
判断他能到达的最左边
接着进行二分即可

代码

【暴力】
通过了80%数据 n^4

//暴力做法
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 510;
int n, m, k;
typedef long long ll;
int s[N][N];
ll res;

int main() {
	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			cin >> s[i][j];
			s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
		}
	for (int x1 = 1; x1 <= n; x1++)
		for (int y1 = 1; y1 <= m; y1++)
			for (int x2 = x1; x2 <= n; x2++)
				for (int y2 = y1; y2 <= m; y2++) {
					int sum = s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
					if (sum <= k)
						res++;
				}
	cout << res << endl;
	return 0;
}

【二分】
通过80%的数据 n^3

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 510;
int n, m, k;
typedef long long ll;
int s[N][N];
ll res;


int sum(int x1, int y1, int x2, int y2) {
	int p = 0;
	p += s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
	return p;
}

int main() {
	cin >> n >> m >> k;
	//读入矩阵的值,并预处理二维前缀和
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			cin >> s[i][j];
			s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] ;
		}
	ll res = 0;
	//枚举上下边界,利用二分求出答案
	for (int i = 1; i <= n; i++)
		for (int j = i; j <= n; j++)
			for (int t = 1; t <= m; t++) {
				int l = 1, r = t;
				while (l < r) {
					int mid = l + r >> 1;
					if (sum(i, mid, j, t) > k)
						l = mid + 1;
					else
						r = mid;
				}
				if (sum(i, l, j, t) <= k)
					res += t - l + 1;
			}
	cout << res << endl;
	return 0;
}

【双指针】
通过了100%数据 n^2

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 510;
int n, m, k;
typedef long long ll;
int s[N][N];

int main() {
	cin >> n >> m >> k;
	//读入矩阵的值,并预处理一维前缀和
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			cin >> s[i][j];
			s[i][j] += s[i - 1][j];
		}
	//枚举上下边界,并利用双指针求出答案
	ll res = 0;
	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 += s[j][r] - s[i - 1][r];
				while (sum > k) {
					sum -= s[j][l] - s[i - 1][l];
					l++;
				}
				res += r - l + 1;
			}
	cout << res << endl;
	return 0;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值