题目链接
个人思路分析
当看到统计有多少个子矩阵所有数的和不超过指定的整数K,直接就想到了二维前缀和。
对于前缀和知识点还不清楚的,可以去浏览下我之前关于前缀和的基础介绍【洛谷】前缀和入门。后面如果这个链接因为我修改了前面博客内容失效了,大家也可以去我主页里->算法做题笔记专栏->洛谷专栏下面去查找这篇博客。
想要求子矩阵的和,那么我们就先对输入的 n*m
的矩阵进行一个预处理,获取该矩阵的前缀和。
当然这题的关键不是去获取前缀和,而是如何去遍历所有满足条件的值。
示例代码
Java版
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class Main {
static long[][] arr;
static int n, m;
static long k;
// Java 的快读
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer st = new StreamTokenizer(br);
static int nextInt() throws IOException {
st.nextToken();
return (int) st.nval;
}
static long nextLong() throws IOException {
st.nextToken();
return (long) st.nval;
}
public static void main(String[] args) throws IOException {
// 数据读入
n = nextInt();
m = nextInt();
k = nextLong();
arr = new long[n + 1][m + 1];
// 预处理前缀和
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
long t = nextLong();
arr[i][j] = arr[i - 1][j] + arr[i][j - 1] - arr[i - 1][j - 1] + t;
}
}
long res = 0;
// 遍历行范围:从 x1 到 x2
for(int x1 = 1; x1 <= n; ++x1) {
for(int x2 = x1; x2 <= n; ++x2) {
// 遍历列范围:从 y1 到 y2
// 当前情况满足条件时,只移动 y2,不移动 y1
for(int y1 = 1, y2 = 1; y2 <= m; ++y2) {
// 当不满足条件时,移动 y1
while (y1 <= y2 && !check(x1, y1, x2, y2)) {
++y1;
}
// 在满足条件的情况下,从 y1 到 y2 区域都是满足的,就不重复计算了
res += (y2 - y1 + 1);
}
}
}
System.out.println(res);
}
// 判断当前是否满足条件
private static boolean check(int x1, int y1, int x2, int y2) {
long sum = arr[x2][y2] - arr[x1 - 1][y2] - arr[x2][y1 - 1] + arr[x1 - 1][y1 - 1];
return sum <= k;
}
}
C/C++版
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 503;
int n, m;
ll k, arr[N][N] = {0}, t;
static int check(int x1, int y1, int x2, int y2)
{
long sum = arr[x2][y2] - arr[x1 - 1][y2] - arr[x2][y1 - 1] + arr[x1 - 1][y1 - 1];
return sum <= k;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
{
cin >> t;
arr[i][j] = t + arr[i - 1][j] + arr[i][j - 1] - arr[i - 1][j - 1];
}
ll res = 0;
for (int x1 = 1; x1 <= n; ++x1)
{
for (int x2 = x1; x2 <= n; ++x2)
{
// 遍历列范围:从 y1 到 y2
// 当前情况满足条件时,只移动 y2,不移动 y1
for (int y1 = 1, y2 = 1; y2 <= m; ++y2)
{
// 当不满足条件时,移动 y1
while (y1 <= y2 && !check(x1, y1, x2, y2))
{
++y1;
}
// 在满足条件的情况下,从 y1 到 y2 区域都是满足的,就不重复计算了
res += (y2 - y1 + 1);
}
}
}
cout << res;
return 0;
}