蓝桥杯:统计子矩阵(十三届省赛C++组)
前言:
这道题目是矩阵类型题目经典题型,解题大体思路是前缀和+双指针扫描,在我这篇博客中
第十三届蓝桥杯省赛C++B组题解_第十三届蓝桥杯b组c++答案_正在黑化的KS的博客-CSDN博客
简单提了一下大致解法,今天刷题时又遇到了一个极其相似的题型,所以写下这篇题解巩固加深记忆。
题目描述
朴素解法
朴素解法其实很简单,其实就是二维前缀和后暴力枚举长和宽,时间复杂度是O(),就这个题目的数据范围而言,不能接受,当然也是可以骗点分的,具体代码很简单,这里就不赘述了。
AC解法
首先,我们这里依然要使用前缀和的技巧,不过这里的前缀和是每列的前缀和,举个例子
g[i][j] 代表的意思是在第j列,第1行到第i行的值。
现在我们枚举上边界和下边界,即当前枚举矩阵是处在第x行(上边界),和y(下边界)之间的。
接着枚举矩形的右端点,我们可以发现,每次当右端点向右移动的时候,矩形的面积是单调递增的,此时如果矩形的面积大于K,则不合要求,固定右端点,此时我们将当前矩形的左端点也向右移动,矩形的面积单调变小,直到举行面积再次小于K,停止移动。
那么我们怎么记录答案呢,可以发现每次移动右端点,再配合移动左端点,达到最终状态后,对于当前上下边界及左右边界的矩形而言,这次移动右端点所带来的贡献应该是
右端点 - 左端点 + 1
因为对于现在这个右端点,向左看,从自身到右端点之间形成的矩形都是面积小于K的,因此贡献应该是左右端点之间的距离,几上面的式子
累加答案即位最终结果
代码
#include <iostream>
#include <algorithm>
#include <cstring>using namespace std ; typedef long long LL ; const int N = 510 ;
int n, m, k ;
int g[N][N] ;int main()
{ios::sync_with_stdio(false) ; cin >> n >> m >> k ; for (int i = 1 ; i <= n ; i ++ ) for (int j = 1 ; j <= m ; j ++ ){cin >> g[i][j] ; g[i][j] += g[i - 1][j] ; // 按列前缀和}LL res = 0 ;for (int x = 1 ; x <= n ; x ++ ) // 枚举上界for (int y = x ; y <= n ; y ++ ) // 枚举下界for (int i = 1, j = 1, sum = 0 ; i <= m ; i ++ ) // i为右端点,j为左端点{sum += g[y][i] - g[x - 1][i] ; while (sum > k && j < i) {sum -= g[y][j] - g[x - 1][j] ; j ++ ; }if (sum <= k) res += i - j + 1 ;}cout << res << endl ;return 0 ;
}
最后附送大家一个相似的练习题
3487. 最小面积子矩阵 - AcWing题库
这里给出我的AC代码以供参考,思路很类似。
#include <iostream>
#include <algorithm>
#include <cstring>using namespace std ; const int N = 110, INF = 1e4 + 10 ;
int g[N][N] ;
int n, m, k ; int main()
{ios::sync_with_stdio(false) ; cin >> n >> m >> k ; for (int i = 1 ; i <= n ; i ++ ) for (int j = 1 ; j <= m ; j ++ ) {cin >> g[i][j] ; g[i][j] += g[i - 1][j] ;}int res = INF ; for (int x = 1 ; x <= n ; x ++ ) for (int y = x ; y <= n ; y ++ ) for (int i = 1, j = 1, sum = 0 ; i <= m ; i ++ ) {sum += g[y][i] - g[x - 1][i] ; while (sum - g[y][j] + g[x - 1][j] >= k) {sum -= g[y][j] - g[x - 1][j] ; j ++ ; }if (sum >= k) res = min(res, (y - x + 1) * (i - j + 1)) ;}if (res == INF) cout << -1 << endl ; else cout << res << endl ;return 0 ;
}