问题概述:
输入:
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12
输出:
19
题目分析:
我们可以将数组存为一个前缀和数组,不同的是这是列的前缀和
如:
1 2 3 4 1 2 3 4
5 6 7 8 存为列的前缀和数组为: 6 8 10 12
9 10 11 12 15 18 21 24
(1)我们可以用两个指针
top:用来遍历每一行 范围:1<=top<=N
bot:用来遍历从top开始的每一行 范围 :top<=bot<=N
每次就把矩阵截取出了一个 (bot-top+1)*M 的子矩阵
如:
1 2 3 4
6 8 10 12 这个矩阵在top=1&&bot = 2时为 6 8 10 12 这个子矩阵
15 18 21 24 15 18 21 24
这样就可以遍历从top开始的每一个 (bot-top+1)*M 维度的子矩阵
(2)接着可以再定义两个指针
l:表示子矩阵的左边界 范围:1<=l<=M
r:表示子矩阵的右边界 范围 : 1<=r<=M
通过l与r的变换 再结合之前 top 与 bot 这四个指针 ,分别定义了矩阵的左边界,右边界,上边界与下边界。
如:top =1 ; bot =2 ; l =1; r=2;
此时截取出来的矩阵为:
8 10
18 21
这样通过这些变换,我们就可以遍历或得所有的子矩阵:
//遍历所有的子矩阵
for (int top = 1; top<=N; top++) {
for (int bot = top; bot <= N; bot++) {
l = r = 1;
for (int r = 1; r <=M; r++) {
//核心逻辑实现
}
}
}
(3)核心功能实现
我们可以用一个sum来保存当前以 [bot,r] 为右下角的子矩阵的和
我们可以推出[bot,r] 为右下角的子矩阵的和:
sum += arr[bot][r] - arr[top-1][r];
如果sum大于K 则让 l 向右移动 缩小矩阵宽度 ,移动至sum<=K为止
此时公式为: sum -= (arr[bot][l]-arr[top-1][l]);
l++;
此时 r-l+1 便所有以 [bot,r] 为右下角的满足条件的子矩阵
如:top=2 bot =2 l =1 r=2 时 此时sum = 11 大于 K(K =10)
1 2 3 4 1 2 3 4
5 6 7 8 arr数组: 6 8 10 12
9 10 11 12 15 18 21 24
则此时 sum =sum - (arr[2][1]-arr[1][1])=11-6-1 = 6; l++
此时sum<K 满足条件 l便可以停止右移
整体代码实现:
#include <iostream>
using namespace std;
long long N, M, K;
int arr[507][507];//列前缀和数组
int main()
{
cin >> N >> M >> K;
for (int i = 1; i <=N; i++) {
for (int j = 1; j <= M; j++) {
cin >> arr[i][j];
arr[i][j] += arr[i - 1][j];
}
}
/*
1 2 3 4 1 2 3 4
5 6 7 8 -> 6 8 10 12
9 10 11 12 15 18 21 24
*/
int l, r;
long long sum = 0;
long long ans = 0;
for (int top = 1; top<=N; top++) {
for (int bot = top; bot <= N; bot++) {
l = r = 1;
sum = 0;
for (int r = 1; r <=M; r++) {
sum += arr[bot][r] - arr[top-1][r];
while (sum > K) {
sum -= (arr[bot][l]-arr[top-1][l]);
l++;
}
ans += r - l + 1;
}
}
}
cout << ans << endl;
return 0;
}