首页 > 编程语言 >【C++二分查找 前缀和 】1292. 元素和小于等于阈值的正方形的最大边长

【C++二分查找 前缀和 】1292. 元素和小于等于阈值的正方形的最大边长

时间:2024-08-20 19:25:03浏览次数:10  
标签:mat 阈值 int mid C++ threshold 1292 vSum left

本文涉及的基础知识点

C++二分查找
C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频

LeetCode1292. 元素和小于等于阈值的正方形的最大边长

给你一个大小为 m x n 的矩阵 mat 和一个整数阈值 threshold。
请你返回元素总和小于或等于阈值的正方形区域的最大边长;如果没有这样的正方形区域,则返回 0 。

示例 1:
输入:mat = [[1,1,3,2,4,3,2],[1,1,3,2,4,3,2],[1,1,3,2,4,3,2]], threshold = 4
在这里插入图片描述

输出:2
解释:总和小于或等于 4 的正方形的最大边长为 2,如图所示。
示例 2:
输入:mat = [[2,2,2,2,2],[2,2,2,2,2],[2,2,2,2,2],[2,2,2,2,2],[2,2,2,2,2]], threshold = 1
输出:0
提示:
m == mat.length
n == mat[i].length
1 <= m, n <= 300
0 <= mat[i][j] <= 104
0 <= threshold <= 105

二分查找、前缀和

预处理好前缀和后,可以在O(1)的时间内,计算矩形之和。
二分查找类型:寻找末端
Check函数的参数返回:[1,min(n,m)]
异常处理:如果Check(ret)不成立,则返回0。
Check函数:枚举各矩形的左上角,如果和小于等于阈值,返回true。否则返回false。Check函数的时间复杂度:O(nm)
总时间复杂度:O(log(min(n,m))nm)

代码

核心代码

template<class T = int>
class CPreSum2 {
public:
	template<class _Pr>
	CPreSum2(int rowCnt, int colCount, _Pr pr) :m_iRowCnt(rowCnt), m_iColCnt(colCount) {
		m_vSum.assign(rowCnt + 1, vector<int>(colCount + 1));
		for (int r = 0; r < rowCnt; r++) {
			for (int c = 0; c < colCount; c++) {
				m_vSum[r + 1][c + 1] = m_vSum[r][c + 1] + m_vSum[r + 1][c] - m_vSum[r][c] + pr(r, c);
			}
		}
	}
	T Get(int left, int top, int right, int bottom)const {
		return m_vSum[bottom + 1][right + 1] - m_vSum[top][right + 1] - m_vSum[bottom + 1][left] + m_vSum[top][left];
	}
	T GetTopLeft(int left, int top) { return Get(left, top, m_iColCnt - 1, m_iRowCnt - 1); }
	vector<vector<T>> m_vSum;
	const int m_iRowCnt, m_iColCnt;
};
template<class INDEX_TYPE>
class CBinarySearch
{
public:
	CBinarySearch(INDEX_TYPE iMinIndex, INDEX_TYPE iMaxIndex):m_iMin(iMinIndex),m_iMax(iMaxIndex) {}
	template<class _Pr>
	INDEX_TYPE FindFrist( _Pr pr)
	{
		auto left = m_iMin - 1;
		auto rightInclue = m_iMax;
		while (rightInclue - left > 1)
		{
			const auto mid = left + (rightInclue - left) / 2;
			if (pr(mid))
			{
				rightInclue = mid;
			}
			else
			{
				left = mid;
			}
		}
		return rightInclue;
	}
	template<class _Pr>
	INDEX_TYPE FindEnd( _Pr pr)
	{
		int leftInclude = m_iMin;
		int right = m_iMax + 1;
		while (right - leftInclude > 1)
		{
			const auto mid = leftInclude + (right - leftInclude) / 2;
			if (pr(mid))
			{
				leftInclude = mid;
			}
			else
			{
				right = mid;
			}
		}
		return leftInclude;
	}
protected:
	const INDEX_TYPE m_iMin, m_iMax;
};

class Solution {
		public:
			int maxSideLength(vector<vector<int>>& mat, int threshold) {
				m_r = mat.size();
				m_c = mat.front().size();
				CPreSum2<int> preSum(m_r, m_c, [&](const int& r, const int& c) {return mat[r][c]; });			
				auto Check = [&](int mid) {
					for (int r = 0; r+mid <= m_r; r++) {
						for (int c = 0; c+mid <= m_c; c++) {
							const int sum = preSum.Get(c, r, c + mid - 1, r + mid - 1);
							if (sum <= threshold) { return true; }
						}
					}
					return false;
				};
				return CBinarySearch<int>(0, min(m_r, m_c)).FindEnd(Check);
			}
			int m_r, m_c;
		};

单元测试

	vector<vector<int>> mat;
		int threshold;

		TEST_METHOD(TestMethod13)
		{
			mat = { {1,1,3,2,4,3,2},{1,1,3,2,4,3,2},{1,1,3,2,4,3,2} }, threshold = 4;
			auto res = Solution().maxSideLength(mat, threshold);
			AssertEx(2, res);
		}
		TEST_METHOD(TestMethod14)
		{
			mat = { {2,2,2,2,2},{2,2,2,2,2},{2,2,2,2,2},{2,2,2,2,2},{2,2,2,2,2} }, threshold = 1;
			auto res = Solution().maxSideLength(mat, threshold);
			AssertEx(0, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

标签:mat,阈值,int,mid,C++,threshold,1292,vSum,left
From: https://blog.csdn.net/he_zhidan/article/details/141023726

相关文章

  • C++——STL——string容器
    string的头文件#include<string>string的初始化1.默认初始化,此时该字符串为空字符串strings1;2.s2是s1的副本strings2(s1)//构造函数3.等价于s3(s1),则s3是s1的副本strings3=s1;4.s4的字面值是"nihao"strings4("nihao");//构造函数5.与上行代码是等价的string......
  • [C++] template+struct 组合使用小技巧
    1.简单说明  struct+template的组合可以让我们使用同一个结构体名称(注意:只是名称相同,但是本质上已经不同了),实现不同的结构体功能,可以将其理解为设计模式中的工程模式。2.代码示例  首先,声明一个枚举类型,用于区别结构体,然后使用template+struct,声明一个结构体,只声明不实现......
  • 小白成长第二天:利用C#调用Halcon初步实现阈值分割方法
        在上篇文章中已经实现了在C#中成功调用Halcon,今天来实现阈值分割,并且利用简单的封装来优化自己的阈值分割方法。一、前期准备创建好工程后,设计一个基本的框架UI(不会创建工程的同志以及没搭好环境的同学,可以看我上一篇),这里我用了两个按钮(button)两个标签(Label)两个文......
  • C++基础用法
    容器vector定义#include<vector>usingnamespacestd;vector<type>name;//type为数据类型,如int,string等,name为vector标识访问通过下标访问通过迭代器访问vector<type>::iteratorit;例如:vector<int>::iteratorit=vi.begin();......
  • 从零开始学习C++(0)
    这是什么?要先学习C++,我们要先了解C++是什么这是WikiPedia的解释,我们来提炼一下:C++是一种高级语言。C++是C语言的扩展升级版。C++是面向对象语言。下载环境简单了解一下后,我们来下载C++编译器环境。目前有很多种编译器,例如:Dev-C++CodeBlocksVSCVSred......
  • C++语言基础|函数重载
    C++语言基础|函数重载1.函数重载1.1函数重载的定义1.1函数重载的示例2.函数重载注意事项3重载函数的二义性3.1绑定(匹配)二义性3.2消除二义性3.3注意事项1.函数重载1.1函数重载的定义函数重载就是两个以上的函数,取相同的函数名,但是形参的个数和类型不同,编......
  • C++容器概览
    容器容器是用来存储数据的序列,它们提供了不同的存储方式和访问模式。STL中的容器可以分为三类:1、序列容器:存储元素的序列,允许双向遍历。vector:动态数组,支持快速随机访问。deque:双端队列,支持快速插入和删除。list:链表,支持快速插入和删除,但不支持随机访问。2、关联容器:存......
  • C++基础
    指针#include<iostream>usingnamespacestd;intmain(){//实际变量的声明intvar=20;//指针变量的声明int*addr;//在指针变量中存储var的地址addr=&var;cout<<"var="<<var<<endl;//输出在指针变量中存储......
  • Ros2 MoveIt2 MoveGroup C++接口
     在MoveIt中,最简单的用户界面是通过 MoveGroupInterface 类。它为用户可能想要执行的大多数操作提供了易于使用的功能,特别是设置关节或姿势目标、创建运动计划、移动机器人、将对象添加到环境中以及从机器人上连接/分离对象。此接口通过ROS主题、服务和操作与 MoveGrou......
  • c++超详细教学(5)变量1
    上一节,我们讲到了换行,空格,以及运算,这一节课,我们讲一个高级一点的东西,叫变量。大家不用慌张,名字听着很玄乎,实际上很简单。上次,我给大家讲到了运算,可是如果你每次都改代码,那就显得太不专业了,为了让大家更加专业,我就给大家讲一讲变量。比如,你每次只算一个数字加一,那么我们就可......