给定一个 N×M𝑁×𝑀 的矩阵 A𝐴,请你统计有多少个子矩阵 (最小 1×11×1,最大 N×M𝑁×𝑀) 满足子矩阵中所有数的和不超过给定的整数 K𝐾?
输入格式
第一行包含三个整数 N,M𝑁,𝑀 和 K𝐾。
之后 N𝑁 行每行包含 M𝑀 个整数,代表矩阵 A𝐴。
输出格式
一个整数代表答案。
数据范围
对于 30%30% 的数据,N,M≤20𝑁,𝑀≤20,
对于 70%70% 的数据,N,M≤100𝑁,𝑀≤100,
对于 100%100% 的数据,1≤N,M≤500;0≤Aij≤1000;1≤K≤2.5×1081≤𝑁,𝑀≤500;0≤𝐴𝑖𝑗≤1000;1≤𝐾≤2.5×108。
输入样例:
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12
输出样例:
19
样例解释
满足条件的子矩阵一共有 1919,包含:
- 大小为 1×11×1 的有 1010 个。
- 大小为 1×21×2 的有 33 个。
- 大小为 1×31×3 的有 22 个。
- 大小为 1×41×4 的有 11 个。
- 大小为 2×12×1 的有 33 个。
暴力 O(N^4):
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int m,n,k,s[N][N],a[N][N];
int main()
{
cin >> m >> n >> k;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
cin >> a[i][j];
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
long long int ans=0;
//四层循环,枚举每一个矩阵,并判断是否符合题解
for (int x1 = 1; x1 <= m; x1 ++)
for (int y1 = 1; y1 <= n; y1 ++)
for (int x2 = x1; x2 <= m; x2 ++)
for (int y2 = y1; y2 <= n; y2 ++) {
if (s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] <= k)
ans ++;
}
cout << ans;
return 0;
}
优化O(n^3):
将左右两边放到一条线上,这时线上的长度就是矩阵的集合。类似于降维度
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int m,n,k,s[N][N],a[N][N];
int main()
{
cin >> m >> n >> k;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
cin >> a[i][j];
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
long long int ans=0;
//将左右边压缩成线性
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
//快指针t,慢指针s1
for(int s1=1,t=1;t<=m;t++)
{
//当形成的矩阵大于目标值时,压缩矩阵的长度(s1++),当不大于k时,
//就没必要压缩了,剩下的矩阵的值一定都小于k,符合的矩阵为:t-s1+1
while(s1<=t&&s[s1-1][i-1]-s[t][i-1]-s[s1-1][j]+s[t][j]>k) s1++;
//剩下t-s1+1个矩阵,因为是左右两边枚举向右扫值
//所以不用担心后续的矩阵会少值的情况
if(s1<=t) ans+=t-s1+1;
}
}
}
cout << ans;
return 0;
}
当矩阵越来越大时,中间的矩阵如果符合题目要求,该怎么加上呢?
想多了,快指针与慢指针枚举过程中不会遗漏任何一个矩阵。
tips
先打暴力,之后再优化。
C++里只要数据次数少于1e8就能跑过。
本题明显只要优化一次就能跑过。