首页 > 编程语言 >C++前缀和算法应用:矩形区域不超过 K 的最大数值和

C++前缀和算法应用:矩形区域不超过 K 的最大数值和

时间:2023-10-24 16:02:08浏览次数:41  
标签:row 前缀 int vPreSum C++ right 矩形 matrix


题目

给你一个 m x n 的矩阵 matrix 和一个整数 k ,找出并返回矩阵内部矩形区域的不超过 k 的最大数值和。
题目数据保证总会存在一个数值和不超过 k 的矩形区域。
示例 1:
输入:matrix = [[1,0,1],[0,-2,3]], k = 2
输出:2
解释:蓝色边框圈出来的矩形区域 [[0, 1], [-2, 3]] 的数值和是 2,且 2 是不超过 k 的最大数字(k = 2)。
示例 2:
输入:matrix = [[2,2,-1]], k = 3
输出:3

提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-100 <= matrix[i][j] <= 100
-105 <= k <= 105

分析

二维前缀和

vPreSum[r][c] 记录以(0,0)开始,宽高为c r的矩形和。vPreSum[r][c] = vPreSum[r - 1][c] + vPreSum[r][c - 1] + matrix[r - 1][c - 1] - vPreSum[r - 1][c - 1];

C++前缀和算法应用:矩形区域不超过 K 的最大数值和_c++

vPreSum[r - 1][c]

绿色实心矩形的和

vPreSum[r][c - 1]

蓝色空心矩形

matrix[r - 1][c - 1]

黄色矩形

vPreSum[r - 1][c - 1]

蓝绿重叠部分

时间复杂度O(nnn*logn)

枚举所有矩形,时间复杂度是O(n^4),超时。三层循环,第一层和第二层循环,枚举左右。第三层循环枚举下,setSum记录了所有合法的t的构成的矩形(left,0,right,t-1)的和。显然iLeftRight - setSum任意元素的值,就是(left,t,right,row)矩形的和。iLeftRight 是矩形(left,0,right,row)的和。对setSum 进行二分。

源码

class Solution {
 public:
 int maxSumSubmatrix(vector<vector>& matrix, int k) {
 m_r = matrix.size();
 m_c = matrix.front().size();
 vector<vector> vPreSum(m_r+1,vector(m_c+1,0)); //vPreSum[r][c] 以(0,0)开始,宽高为c r的矩形
 for (int r = 1; r <= m_r; r++)
 {
 for (int c = 1; c <= m_c; c++)
 {
 //令r=1,c=1 vPreSum[1][1] = 0 +0 + matrix[0][0] -0
 //令r=2,c=2 vPreSum[2][2] = vPreSum[1][2] +vPreSum[2][1] + matrix[1][1] + vPreSum[1][1]
 vPreSum[r][c] = vPreSum[r - 1][c] + vPreSum[r][c - 1] + matrix[r - 1][c - 1] - vPreSum[r - 1][c - 1];
 }
 }
 int iMin = INT_MIN;
 //vector<vector> vDebug(m_r, vector(m_c,INT_MIN));
 for (int left = 0; left < m_c ; left++ )
 {
 for (int right = left ; right < m_c; right++ )
 {
 //row和right 是右下角的坐标
 set setSum = { 0 };
 for (int row = 0; row < m_r; row++)
 {
 const int iLeftRight = vPreSum[row + 1][right + 1]• vPreSum[row + 1][left];
 //iLeftRight - x <=k ==> -x <= k - iLeftRight ==> x >= iLeftRight-k
 auto it = setSum.lower_bound(iLeftRight - k);
 if (setSum.end() != it)
 {
 iMin = max(iMin, iLeftRight - *it);
 //vDebug[row][right] = iLeftRight - *it;
 }
 setSum.emplace(iLeftRight);
 }
 }
 }
 return iMin;
 }
 int m_r, m_c;
 };

测试用例

template
 void Assert(const vector& v1, const vector& v2)
 {
 if (v1.size() != v2.size())
 {
 assert(false);
 return;
 }
 for (int i = 0; i < v1.size(); i++)
 {
 assert(v1[i] == v2[i]);
 }
 }template
 void Assert(const T& t1, const T& t2)
 {
 assert(t1 == t2);
 }
 int main()
 {
 vector<vector> matrix = { {1, 2, 3}, { 4,5,6 } };
 int k = 2;
 auto res = Solution().maxSumSubmatrix(matrix, k);
 Assert(res, 2);
 matrix = { {1, 0, 1}, { 0,-2,3 } };
 k = 2;
 res = Solution().maxSumSubmatrix(matrix, k);
 Assert(res, 2);
 matrix = { {2,2,-1} };
 k = 3;
 res = Solution().maxSumSubmatrix(matrix, k);
 Assert(res, 3);
 CConsole::Out(res);
 }

扩展阅读


相关下载

想高屋建瓴的学习算法,请下载《闻缺陷则喜算法册》doc版

鄙人想对大家说的话

闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。

墨家名称的来源:有所得以墨记之。

如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17

C++前缀和算法应用:矩形区域不超过 K 的最大数值和_开发语言_02


标签:row,前缀,int,vPreSum,C++,right,矩形,matrix
From: https://blog.51cto.com/u_15724537/8005230

相关文章

  • 时间复杂度O(40n*n)的C++算法:修改图中的边权
    1.12.1.题目给你一个n个节点的无向带权连通图,节点编号为0到n-1,再给你一个整数数组edges,其中edges[i]=[ai,bi,wi]表示节点ai和bi之间有一条边权为wi的边。部分边的边权为-1(wi=-1),其他边的边权都为正数(wi>0)。你需要将所有边权为-1的边都修改为范......
  • 在C++中,互斥变量(std::mutex)是用于保护共享资源的重要工具,但它们确实有一些局限性,其中
    在C++中,互斥变量(std::mutex)是用于保护共享资源的重要工具,但它们确实有一些局限性,其中之一是无法保证包含指针的区域的多线程安全。这是因为互斥锁本质上只能保护它们所保护的代码块,而不会考虑指针指向的数据。下面是一些与互斥锁和指针相关的常见问题和注意事项:共享数据的复制:......
  • C++_Cmake的使用
    C++系统版本、软件依赖版本、组件LSB(全称:LinuxStandardsBase)LSBsharedobjectELF是ExecutableLinkableFormat的缩写,是Linux的链接、可执行、共享库的格式标准,COFF:CommonObjectCOFF(通用对象文件格式) 编译器:简单构建gcc编译流程分为4个步骤,分......
  • C++初识(续篇)
    1.2注释作用:在代码中加一些说明和解释,方便自己或其他程序员阅读代码两中格式单行注释:通常放在一行代码的上方,或者一条语句的末尾,对该行代码说明//这样的是单行注释多行注释:通常放在一段代码的上方,对该段代码做整体说明/*这种的是多行注释可以写好多行*/......
  • c++中的宏#define用途
    宏的一些作用,包括但不限于这些定义一个变量、字符串、类型定义一个函数、条件表达式条件编译、调试信息,异常类定义结构体、命名空间定义模版、枚举、函数对象#define宏定义在C++中用于定义常量、函数、条件编译、字符串、条件表达式、变量、注释、调试信息、类型、函数等,下面是一些......
  • C++的编译链接与在vs中build提速
    通过gcc或msvc,clang等编译器编译出来的C++源文件是.o文件。在windows上也就是PE文件,linux为ELF文件,在这一步中,调用其它代码文件中的函数的函数地址是未知的(00000),等到链接之后才会替换掉函数地址的linux,windows可执行文件(ELF、PE)C++是如何编译的C/C++编译过程主要分为4个过程......
  • C++常用语法知识--数据类型
    C++常用语法知识--数据类型C++为用户提供了7种基本C++数据类型:类型关键字字节大小布尔型bool1字符型char1有符号字符型signedchar1无符号字符型unsignedchar1整型int4有符号整型signedint4无符号整型unsignedint4短整型int2......
  • C++常用语法知识-- std::istringstream
    C++常用语法知识--std::istringstream介绍std::istringstream是C++标准库中的一个类,它用于从字符串中提取数据,并将数据转换为不同的数据类型。通常从字符串中解析数据,例如整数、浮点数等。使用方法创建std::istringstream对象,首先,需要创建一个std::istringstream对象,将......
  • C++常量
    上篇文章:C++中的变量https://blog.51cto.com/u_15965130/7979028常量常量,顾名思义,是常数的量,也就是说在定义的时候初始化一个值,这个值时固定的,在以后的程序运行中不可修改。常量修饰符constC++中常量的修饰符是const关键字。即,在变量的基础上添加const关键字,表示这个量是常量,......
  • C++常用知识语法--双冒号
    C++常用知识语法--双冒号作用域符号::的前面一般是类名称,后面一般是该类的成员名称,C++为避免不同的类有名称相同的成员而采用作用域的方式进行区分例如:A、B表示两个类,在A、B中都有成员member。A::member就表示类A中的成员memberB::member就表示类B中的成员member全局作用......