蓝桥杯求二维矩阵
我们要求一个矩阵内一个任意的子矩阵的数的和,我们就可以用二维前缀和。
二位前缀和:
map[i][j]=map[i][j-1]+map[i-1][j]-map[i-1][j-1]+map[i][j];
(x1,y1)到(x2,y2)的和:
-
map[x2][y2]-map[x2][y1]-map[x1][y2]+map[x1][y1]
(不包含矩阵边时) -
map[x2][y2]-map[x2][y1-1]-map[x1-1][y2]+map[x1-1][y1-1]
(包含矩阵边时)、
暴力求解:o(n^3)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1000000007, N = 5e2 + 10;
int a[N][N], sum[N][N], ans;
int main() {
int n, m, kk;
cin >> n >> m >> kk;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> a[i][j];
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] + a[i][j] - sum[i - 1][j - 1];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = i; k <= n; k++) {
for (int l = j; l <= m; l++) {
int temp = sum[k][l] - sum[k][j - 1] - sum[i - 1][l] + sum[i - 1][j - 1];
// cout << temp << " ";
if (temp <= kk) {
ans++;
} else {
break;
}
}
}
}
}
cout << ans;
return 0;
}
优化:
1)枚举子矩阵的 左边界i 和 右边界j,
2)用 快指针t 枚举 子矩阵的下边界,慢指针s 维护 子矩阵的上边界 (s ≤≤ t)
3)如果得到的子矩阵的权值和 大于 k,则慢指针s 前进,而子矩阵和必将单调不增
4)慢指针s 继续前进(如图),直到 子矩阵的和 不大于k,慢指针没必要前进了,因为该子矩阵的所有宽度为 j - i + 1 的子矩阵(总共 t - s + 1 种)一定满足要求,更新该情况对答案的贡献 t - s + 1;反之,如果慢指针s越界(s > t),则不操作,直接进入下层循环
复杂度:O(N3)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1000000007, N = 5e2 + 10;
int a[N][N], sum[N][N];
LL ans;
int main() {
int n, m, kk;
cin >> n >> m >> kk;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> a[i][j];
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] + a[i][j] - sum[i - 1][j - 1];
}
}
for (int i = 1; i <= m; ++i) {
for (int j = i; j <= m; ++j) {
for (int s = 1, f = 1; f <= n; f++) {
while (s <= f && sum[f][j] - sum[f][i - 1] - sum[s - 1][j] + sum[s - 1][i - 1] > kk) {
s++;
}
if (s <= f) {
ans += f - s + 1;
}
}
}
}
cout << ans;
return 0;
}