统计子矩阵
统计子矩阵
给定一个 的矩阵 ,请你统计有多少个子矩阵 (最小 ,最大 ) 满足子矩阵中所有数的和不超过给定的整数 ?
输入格式
第一行包含三个整数 和 。
之后 行每行包含 个整数,代表矩阵 。
输出格式
一个整数代表答案。
数据范围
对于 的数据,,
对于 的数据,,
对于 的数据,。
输入样例:
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12
输出样例:
19
样例解释
满足条件的子矩阵一共有 ,包含:
- 大小为 的有 个。
- 大小为 的有 个。
- 大小为 的有 个。
- 大小为 的有 个。
- 大小为 的有 个。
解题思路
参考 [COCI2021-2022#6] Zemljište 中枚举子矩阵的方法,即枚举子矩阵的上边界、下边界、右边界,然后通过二分或双指针来确定左边界,这样时间复杂度就能做到 或 。记录这两题主要想告诉自己,枚举子矩阵不要总是那么死板去想着枚举左上角和右下角两个坐标。
下面给出双指针做法的正确性证明就可以了。首先矩阵中的元素均是非负整数,因此当确定了子矩阵的上边界 ,下边界 ,右边界 后,随着 增加(向右移动),那么构成的矩阵 的和也会递减,因此满足单调性。假设此时左边界 是满足构成矩阵和不超过 的最靠左的位置,那么当 向右移动到 , 只会向右移动。否则假设 向左移动到 ,由于矩阵 的和不超过 ,因此矩阵 的和也不超过 ,而 ,这就与假设矛盾了。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
const int N = 510;
typedef long long LL;
int s[N][N];
int main() {
int n, m, k;
scanf("%d %d %d", &n, &m, &k);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", &s[i][j]);
s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
}
}
LL ret = 0;
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
for (int u = 1, v = 1; u <= m; u++) {
while (v <= u && s[j][u] - s[i - 1][u] - s[j][v - 1] + s[i - 1][v - 1] > k) {
v++;
}
ret += u - v + 1;
}
}
}
printf("%lld", ret);
return 0;
}
参考资料
AcWing 4405. 统计子矩阵(秋招每日一题):https://www.acwing.com/video/4234/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17776389.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效