⭐️前面的话⭐️
本篇文章介绍来自2022年蓝桥杯真题之统计子矩阵,算法考点为前缀和和滑动窗口,展示语言java。
📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
📆首发时间:🌴2023年3月28日🌴
✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《算法》,📚《算法导论》
💬参考在线编程网站:🌐牛客网🌐力扣
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
⭐️统计子矩阵⭐️
🔐题目详情
统计子矩阵
问题描述
给定一个
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
N
N 行每行包含
M
M
M 个踝数, 代表知阵
A
A
A.
输出格式
一个㮛数代表暜案。
样例输入
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12
样例输出
19
样例说明
满足条件的子知阵一共有 19 , 包含:
大小为
1
×
1
1 \times 1
1×1 的有 10 个。
大小为
1
×
2
1 \times 2
1×2 的有 3 个。
大小为
1
×
3
1 \times 3
1×3 的有 2 个。
大小为
1
×
4
1 \times 4
1×4 的有 1 个。
大小为
2
×
1
2 \times 1
2×1 的有 3 个。
评测用例规模与约定
对于
30
%
30 \%
30% 的敞据,
N
,
M
≤
20
N, M \leq 20
N,M≤20.
对于
70
%
70 \%
70% 的数据,
N
,
M
≤
100
N, M \leq 100
N,M≤100.
对于
100
%
100 \%
100% 的数据,
1
≤
N
,
M
≤
500
;
0
≤
A
i
j
≤
1000
;
1
≤
K
≤
250000000
1 \leq N, M \leq 500 ; 0 \leq A_{i j} \leq 1000 ; 1 \leq K \leq 250000000
1≤N,M≤500;0≤Aij≤1000;1≤K≤250000000.
运行限制
- 最大运行时间: 1 s
- 最大运行内存: 256 M 256 \mathrm{M} 256M
💡解题思路
思路1:二维前缀和,不知道咋高效率枚举两个点,超时了,预计能过70%。
思路2:求每一列的前缀和,再枚举矩阵纵向长的长度,使用滑动窗口枚举横向长度,注意答案爆了
i
n
t
int
int。
🔑源代码
70分:二维前缀和
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
//二维前缀和
// s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j]
const int N = 520;
LL s[N][N], a[N][N];
int n, m;
LL k;
int main()
{
cin >> n >> m >> k;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> a[i][j];
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
int ans = 0;
//枚举两个点
for (int li = 1; li <= n; li++)
{
for (int lj = 1; lj <= m; lj++)
{
for (int ri = li; ri <= n; ri++)
{
for (int rj = lj; rj <= m; rj++)
{
LL cur = s[ri][rj] - s[li - 1][rj] - s[ri][lj - 1] + s[li - 1][lj - 1];
if (cur <= k) ans++;
else break;
}
}
}
}
cout << ans << endl;
return 0;
}
满分做法:按列求前缀和+滑动窗口
c++
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 520;
typedef long long LL;
//求每一列的前缀和,枚举矩阵上限长度,最后使用滑动窗口求(100分)
LL s[N][N];
int n, m;
LL k;
int main()
{
cin >> n >> m >> k;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
int a;
cin >> a;
s[i][j] = s[i - 1][j] + a;
}
}
LL ans = 0;
//枚举竖边
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
//在竖边从左到右滑动窗口
int l = 1, r = 1;
LL sum = 0;
while (r <= m)
{
//累加当前窗口的和,即在第r列,第i-j行之间的和
sum += s[j][r] - s[i - 1][r];
//如果超出k,将左窗口缩小
while (sum > k)
{
//取消左端点的贡献
sum -= s[j][l] - s[i - 1][l];
l++;
}
ans += r - l + 1;
r++;
}
}
}
cout << ans << endl;
return 0;
}
满分做法:java版本
import java.util.*;
import java.io.*;
public class Main
{
static final int N = 520;
static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static final PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static long[][] s = new long[N][N];
static int n, m;
static long k;
public static void main(String[] args) throws IOException {
String[] ss = br.readLine().split(" ");
n = Integer.parseInt(ss[0]);
m = Integer.parseInt(ss[1]);
k = Long.parseLong(ss[2]);
for (int i = 1; i <= n; i++) {
ss = br.readLine().split(" ");
for (int j = 1; j <= m; j++) {
long a = Long.parseLong(ss[j - 1]);
s[i][j] = s[i - 1][j] + a;
}
}
//枚举纵向边长
long ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
//滑动窗口
int l = 1;
int r = 1;
long sum = 0;
while (r <= m) {
sum += s[j][r] - s[i - 1][r];
while (sum > k) {
sum -= s[j][l] - s[i - 1][l];
l++;
}
ans += r - l + 1;
r++;
}
}
}
out.println(ans);
out.close();
}
}
🌱总结
前缀和+滑动窗口