当前位置: 首页 > news >正文

动态规划的“降维”艺术:二维前缀和与哈希表的终极共鸣

哈喽,各位,我是前端L。

在我们的DP(及其衍生技巧)探险中,“前缀和 + 哈希表”这对黄金搭档,已经在一维世界里证明了它们的惊人威力,无论是精确计数(LC 560)、判定存在性(LC 523),还是处理模运算(LC 974),都游刃有余。

今天,我们将把这套强大的组合拳,“升维”到二维矩阵的战场!我们要解决的问题是:在一个二维矩阵中,找出所有“元素和恰好等于目标值 K”的子矩阵,并计算它们的总数量

这道题,是二维前缀和思想与一维子数组和计数技巧的完美结合,是对我们“问题转化”和“模型复用”能力的终极考验。

力扣 1074. 元素和为目标值的子矩阵数量

https://leetcode.cn/problems/number-of-submatrices-that-sum-to-target/

题目分析: 给定一个二维矩阵 matrix 和一个整数 target,返回子矩阵元素总和等于 target数量

  • 子矩阵:由四个坐标 (r1, c1) (左上角) 和 (r2, c2) (右下角) 定义的连续矩形区域。

思路一:二维前缀和的“初级应用” (O(m²n²))

我们首先想到,可以用二维前缀和 preSum[i][j] 来快速计算任意子矩阵的和(回顾 LC 304)。 sum(r1, c1, r2, c2) = preSum[r2+1][c2+1] - preSum[r1][c2+1] - preSum[r2+1][c1] + preSum[r1][c1]

有了这个 O(1) 的求和工具,我们就可以暴力枚举所有的子矩阵:

  • r1, c1 遍历所有可能的左上角。

  • r2, c2 遍历所有可能的右下角 (r2 >= r1, c2 >= c1)。

  • 计算子矩阵和,如果等于 target,计数器加1。

这个方法需要四层循环,时间复杂度是 O(m²n²),对于较大的矩阵来说,依然无法接受。

思路二:“降维打击” + “LC 560 模型复用” (O(m²n) or O(mn²))

O(m²n²) 的瓶颈在于同时枚举了四个坐标。我们能不能固定其中两个,把问题简化?

核心思想:枚举子矩阵的“上下边界”!

  1. 固定上下边界:我们先确定子矩阵的顶行 r1底行 r2 (r1 <= r2)。

  2. “压扁”成一维:对于固定的 r1r2,我们可以计算出一个一维数组 rowSums,其中 rowSums[c] 表示在c,从 r1 行到 r2 行所有元素的rowSums[c] = sum(matrix[r][c] for r from r1 to r2)

  3. “Aha!”时刻:现在,在这个一维数组 rowSums 中,寻找有多少个连续子数组 rowSums[c1...c2],其恰好等于 target? 这不就是我们已经完美解决的 LC 560. 和为 K 的子数组 吗?!

算法流程:

  1. (可选但推荐)预计算二维前缀和 preSum。虽然不是直接用它O(1)查子矩阵,但它可以帮助我们快速计算步骤2中的 rowSums 数组。 rowSums[c] = preSum[r2+1][c+1] - preSum[r1][c+1] - (preSum[r2+1][c] - preSum[r1][c]) (这个公式复杂了) 更优的 rowSums 计算:在 r2 的循环内部,增量计算 rowSums。当 r2r1 开始递增时,rowSums 数组只需要在前一次 r2 的基础上,加上当前 matrix[r2] 这一行的元素即可。这样避免了重复计算。

  2. 初始化总计数 totalCount = 0

  3. 外层循环:遍历所有可能的顶行 r1 (从 0m-1)。

  4. 中层循环:遍历所有可能的底行 r2 (从 r1m-1)。 a. 计算/更新一维 rowSums 数组: 对于当前的 r1r2,计算出 rowSums[0...n-1]。 * (推荐做法)如果 r2 == r1rowSums 就是 matrix[r1] 这一行。 * 如果 r2 > r1rowSums 可以在上一步 r2-1rowSums 基础上,累加上 matrix[r2] 这一行的值。 b. 内层问题 (调用 LC 560 逻辑):对当前得到的 rowSums 数组,应用“前缀和 + 哈希表”的技巧,计算出其中有多少个子数组的和等于 target。将这个计数累加到 totalCount 中。

  5. 所有 (r1, r2) 遍历完毕后,totalCount 就是最终答案。

代码实现

#include <vector>
#include <unordered_map>class Solution {
public:int numSubmatrixSumTarget(vector<vector<int>>& matrix, int target) {int m = matrix.size();int n = matrix[0].size();int totalCount = 0;// 外层循环:固定上边界 r1for (int r1 = 0; r1 < m; ++r1) {// rowSums 存储 [r1...r2] 行之间,每一列的和vector<long long> rowSums(n, 0); // 中层循环:固定下边界 r2for (int r2 = r1; r2 < m; ++r2) {// 更新 rowSums 数组(增量计算)for (int c = 0; c < n; ++c) {rowSums[c] += matrix[r2][c];}// --- 内层问题:LC 560 ---// 对 rowSums 数组,应用前缀和+哈希表,计算和为 target 的子数组个数unordered_map<long long, int> preSumFreq;preSumFreq[0] = 1;long long currentSum = 0;for (long long sum : rowSums) {currentSum += sum;long long needed = currentSum - target;if (preSumFreq.count(needed)) {totalCount += preSumFreq[needed];}preSumFreq[currentSum]++;}// --- 内层问题结束 ---}}return totalCount;}
};

(注:使用 long long 防止 rowSumscurrentSum 溢出)

总结:降维打击,模型复用的力量

今天这道题,是二维问题转化为一维问题的绝佳典范。它深刻地展示了:

面对高维度问题时,尝试通过“固定部分维度”的方式,将其“拍扁”成我们熟悉的低维度问题,是一种极其强大的解题策略。

我们没有直接在二维dp表上硬磕,而是巧妙地:

  1. 枚举了子矩阵的范围 (r1, r2)。

  2. 将这个范围内的矩阵压缩成了一个一维数组 (rowSums)。

  3. 在这个一维数组上,复用了我们已经掌握的“前缀和+哈希表”模型。

这不仅仅是解出了一道题,更是我们算法思维武器库的一次重要升级。掌握了这种“降维 + 模型复用”的打法,你将能应对更多看似无从下手的复杂问题。

咱们下期见~

http://www.dtcms.com/a/530376.html

相关文章:

  • 大气 网站模板淘宝网站建设步骤
  • 保定网站维护公司成都企业网站
  • wordpress建教学网站大连品牌网站建设公司
  • 手机网站模板下载免费crm管理系统销售
  • 网站的域名能修改么北京商场排名
  • 企业做电商网站创建自己的网站能干什么
  • 网站创建软件海口智能建站详情
  • 一个可以做行程的网站wordpress微信群二维码
  • 电子商务网站名称apache 写wordpress
  • 低价代网站网站网站制作公司哪家好
  • 网络编程就是做网站么湖南人文科技学院是几本
  • 建设网站商城宁夏 网站开发
  • 有建网站的公司吗网站建设的技术风险分析与规避
  • 《玩转Docker》[应用篇12]:Docker安装思源笔记使用指南
  • 镇江 网站建设网络舆情现状分析
  • 有名的网站制佛山市网络seo推广公司
  • 广东网站建设报价官网软装设计收费标准
  • 程序地址空间
  • 做企业门户网站网站建好后维护麻烦吗
  • 雅菲奥朗人工智能知识墙分享(三):『AI算力:人工智能时代的“核心引擎”』
  • 如何判断网站是否被收录wordpress加载视频教程
  • 手机版网站系统网站建设和媒体渠道
  • 网站加速 wordpresswordpress媒体库一直转圈
  • Linux 离线迁移conda R虚拟环境教程
  • 过界女主个人做网站的班级网站的建设
  • 杭州公司网站建设套餐怎样申请建立自助网站
  • 网站备案号查询网温州论坛官方网
  • 做网站百度推广南沙网站建设公司哪家好
  • 重庆网站建设推荐网站营销推广有哪些
  • 网站控制做百度网站需不需要备案吗