C++算法进阶系列之倍增算法 ST 表

news2024/11/16 10:41:28

1. 引言

前文使用倍增算法实现了快速求幂的运算,本文继续讲解ST表,ST表即倍增表,本质就是动态规划表,记忆化了不同子问题域中的结果,用于实时查询。只是动态规划过程和传统的稍有点不一样,采用了倍增思想。ST表往往用于存储子区间信息,如某区间的最值……

是不是所有的区间问题都可以使用ST表?

某个区间查询问题是否适用ST表,在于其进行的操作是否允许区间重叠。如下图所示:

4.png

如求 [1,6]区间的最大值,可以使用如下 2 种方案:

  • 直接在[1,6]子区间内使用求最值算法,找出最值。这个算法较易实现,一个循坏语句就搞定。

5.png

  • 如果已经知道了区间[1,5]和区间[3,6]的最大值,这两个区间可认为是区间[1,6]的子区间,且两者有重叠部分,如图可知区间[1,6]最大值是两个子区间中的较大值,即为 10。类似于这样的区间关系,是可以使用 ST表实现的。

6.png

为什么称ST表为倍增表?

整个数组是一个区间,则分割成小区间的方案可以是:

  • 长度为 1 的区间:[0,0],[1,1],[2,2]……
  • 长度为 2 的区间:[0,1],[1,2],[2,3]……
  • 长度为 3 的区间:[0,2],[1,3],[2,4]……
  • 长度为 4 的区间:[0,3],[1,4],[2,5]……
  • 长度为 5 的区间……

还可以划分出长度为6、7、8……的区间,如此分肯定是可行的,但是显得过于零碎、过多,维护代价较高。倍增法分割的原则是按长度为 1、2、4、8、16……分割,也就是按 2 的幂次方分割,这便是倍增表的由来。

  • 长度为 1 的区间:[0,0],[1,1],[2,2]……
  • 长度为 2 的区间:[0,1],[1,2],[2,3]……
  • 长度为 4 的区间:[0,3],[1,4],[2,5]……
  • 长度为8 的区间:[0,7],[1,8],[2,9]……

为什么说这种方案是可行的?

根据前面的分析可知,如果知道当前区间的子区间的最大值且子区间之间连续或重叠的,则当前区间的值可由子区间推导出来。从倍数关系来看,长度为 8 的区间可分割成 2 个长度为 4 的区间,长度为 4 的区间可分割成 2 个长度为 2 的区间……

如下图所示,任何一个区间都可以分割成两个与之有关联的子区间,从而减少更多细碎区间的存在。

7.png

2. 生成 ST 表

区间应该有 3 个属性,左端点(left)、右端点(right)、此区间的最大值。以下分别分析长度不同时区间的左端与右端的关系。

先以长度为 8 的区间[0,7]为例探讨左端与右端的关系:

  • left=0,right=left+23-1=0+8-1=7

以长度为 4 的区间[4,7]为例探讨左端与右端的关系:

  • left=4,right=left+22-1=4+4-1=7

以长度为 2 的区间[4,5]为例探讨左端与右端的关系:

  • left=4,right=left+21-1=4+2-1=5

以长度为 1 的区间[4,4]为例探讨左端与右端的关系:

  • left=4,right=left+01-1=4+1-1=4

再观察求幂运算中指数的通用性:区间长度为 1 时指数为 0;区间长度为 2 时指数为 1;区间长度为 4 时指数为 2;区间长度为 8 时指数为 3……也就是如果知道了left以及区间长度则可计算出右区间的大小,右区间是动态值。

9.png

对于长度为 10的数列而言,到底能划分成多少个不等的区间?

从上述例子来看,数组长度为 10 时,区间最大长度为 8也就是 23 就可以了,其值是 log10(以 2 为底数) 向下取整。有了上述推导的支撑,如可创建 ST就应该呼之欲出了。

创建一个二维数组,命名为 ST表。行坐标表示左端,列坐标并不直接表示右端,而是表示指数,有了指数信息即可以表示区间长度,又能计算出右端大小。如下图 st[0][0]表示的left=0、right=left+20-1=0,即区间[0,0]

10.png

数组存储对应区间的最大值,根据上述的推论可知,区间长度为1的最大值为自己。

11.png

right=1即表示长度为 2 的区间,此时,如下图 st[0][1]的值应该如何推导出来。

12.png

回到上文中的推导理论,知道区间长度为 2 的最大值,可以是它的 2 个长度为 1 的子区间的中的最大值。如下图所示,区间[0,1]的最大值是 max([0,0],[1,1] )的最大值,即为 8

13.png

问题是如何写出动态转移公式?

再分析一下:

  • 求指数为 1的区间值,得从指数为 0的区间找,首先,指数要降级。
  • 左边子区间[0,0]left值和区间[0,1]的左值相同,右边子区间[1,1]的左端值是左子区间的left+20=0+1=1

如此,动态转移公式也就可以出来了:

  • right=0时,st[left][right]=nums[left]
  • right!=0时,st[left][right]=max(st[left][right-1],st[left+2right-1][right-1]

编码实现:

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
//st 表
int st[100][10];
//原数组
int nums[100];
//实际长度
int n,col;
/*
*  初始化
*/
void init() {
	for(int i=0; i<n; i++) {
		scanf("%d",&nums[i]);
		st[i][0]=nums[i];
	}
}
/*
* 创建 ST 表
*/
void setSt() {
	col =(int)(log(n)/log(2));
	for(int j=1; j<=col; j++)
		for (int i=0; i<n-(1<<j)+1; i++)
			st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
/*
*输出ST表
*/
void show() {
	for(int i=0; i<n; i++) {
		for(int j=0; j<=col; j++) {
			cout<<st[i][j]<<"\t";
		}
		cout<<endl;
	}
}
int main() {
	scanf("%d",&n);
	init();
	setSt();
	show();
	return 0;
}

3. 查询

st表的创建,目的是快速查询某区间的最大值,下面讨论如何准确定位区间,以及找到最大值。

如下图所示,如果用户输入的区间是[0,3],则运气很好,因为恰好有这么一个区间。那么如何得到 st[i][j]i和j的值?

显然i=0,求 j的表达式为:i+2j-1=3,则 2j=4-0=4;j=log4(以 2 为底); j=2,也就直接从 st[0][2]得到最大值。

9.png

但是,如果输入的区间是[0,6],不存在这么一个区间,则需要从能作为此区间的子区间中查找。

假设在st[i][j]中,i=l,j=p,len=r-l+1(区间长度),找最大的p应满足2p <= len(以l为起点的st表数据覆盖[l,r]中数足够多),则p=int(log(len)/log(2))

左子区间为 st[l][p],右子区间为 st[x][r]。如何求x,对于st[x][r],应有:[x][x+pow(2,p)-1]<=>[x][r]。所以, x+pow(2,p)-1=r,移项,得:x=r+1-pow(2,p),显然,x>=l ( l+pow(2,p)-1<=r,x+pow(2,p)-1=r,故x>=l)

综上[l,r]的最大值 = max( f[l][p] , f[x][R] )

完整代码:

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
//st 表
int st[100][10];
//原数组
int nums[100];
//实际长度
int n,m,col;
/*
*  初始化
*/
void init() {
	for(int i=0; i<n; i++) {
		scanf("%d",&nums[i]);
		st[i][0]=nums[i];
	}
}
/*
* 创建 ST 表
*/
void setSt() {
	double a= log(5);
	cout<<a<<endl;
	col =(int)(log(n)/log(2));
	for(int j=1; j<=col; j++)
		for (int i=0; i<n-(1<<j)+1; i++)
			st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
/*
*输出ST表
*/
void show() {
	for(int i=0; i<n; i++) {
		for(int j=0; j<=col; j++) {
			cout<<st[i][j]<<"\t";
		}
		cout<<endl;
	}
}
int find() {
	int l,r,p,x;
	for(int i=1; i<=m; i++) {
		scanf("%d%d",&l,&r);
		p=(int)(log(r-l+1)/log(2));
		x=r-(1<<p)+1;
		int res= max( st[l][p], st[x][p]);
		cout<<res<<endl;
	}
}
int main() {
	scanf("%d%d",&n,&m);
	init();
	setSt();
	show();
	find();
	return 0;
}

测试结果:

15.png

4. 总结

区间查询有很多方式,ST是不错的选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1012490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文带你走进软件测试的大门

目录 前言 需求 用户需求 软件需求 从测试人员的角度看需求 测试用例 测试环境 测试数据 预期结果 操作步骤 为什么要有测试用例 Bug的概念 世界上的第一个bug bug的定义 开发模型和测试模型 软件的生命周期 开发模型 瀑布模型 螺旋模型 增量、迭代 敏捷 …

服务器搭建(TCP套接字)-基础版(客户端)

一、socket 1.1、vim man查看socket :!man socket1.2、 依赖的头文件 #include <sys/types.h> #include <sys/socket.h>1.3、原型 int socket(int domain, int type, int protocol);domain说明AF_INETIPV4协议AF_INET6IPV6协议AF_LOCALUnix域协议 type说明S…

华为云云耀云服务器L实例评测|单节点环境下部署ClickHouse21.1.9.41数据库

文章目录 前言云耀云服务器L实例简介clickhouse数据库简介 一、配置环境购买云耀云服务器L实例查看云耀云服务器L实例状态重置密码查看弹性公网IP地址 FinalShell连接服务器二、搭建ClickHouse单机服务下载ClickHouse安装包解压安装依次解压启动clickhouse相关目录 三、允许远程…

为什么IPIDE代理IP可以帮助TikTok引流?

近期&#xff0c;TikTok菲律宾站点保温杯30天销量破30万&#xff0c;GMV达到近千万人民币。在当今社交媒体迅速崛起的时代&#xff0c;营销策略也在逐渐改变。TikTok作为一款非常热门的短视频社交应用&#xff0c;拥有庞大的用户群体&#xff0c;为跨境企业在国际市场上推广产品…

5个免费样机素材网站,设计必备,赶紧马住!

5个设计师必备的样机素材网站&#xff0c;免费下载~ 1、菜鸟图库 https://www.sucai999.com/searchlist/3217.html?vNTYxMjky 菜鸟图库有多种类型的设计素材&#xff0c;像平面、电商、UI、办公等素材这里面都能找到&#xff0c;样机素材非常丰富&#xff0c;比如服装的、电子…

Arcgis在属性表中添加外部Excel信息

Arcgis在属性表中添加外部Excel信息 现需要把EXCEL中这个每个像元的叶面积指数值&#xff0c;导入每个像元的属性表中。 在点的图层上右击&#xff0c;找到连接和关联&#xff0c;点击连接 字段是用于连接属性表和EXCEL的键&#xff0c;两个字段下的数据都是主键&#xff0…

揭秘梦幻般的Glam风格是什么?

如果你热爱一切漂亮、奢华和总体而言都非常华丽&#xff0c;那么华丽风格可能非常适合你。这种风格从传统的外观出发&#xff0c;通过大量的装饰细节增添了一些华丽的元素&#xff0c;创造出一个令人惊叹、闪闪发光、全方位优雅的外观。如果华丽风格引起了你的兴趣&#xff0c;…

微信小程序自动化发布

参考:https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html 参考:https://www.npmjs.com/package/miniprogram-ci npm install miniprogram-ci -S上传文件 xx.js const isNodeJs typeof process ! undefined && process.release ! null &&…

RabbitMQ消息可靠性(二)-- 消费者消息确认

和生产者的消息确认机制不同&#xff0c;因为消息接收本来就是在监听消息&#xff0c;符合条件的消息就会消费下来。 所以&#xff0c;消息接收的确认机制主要存在三种模式&#xff1a; ①自动确认&#xff0c; 这也是默认的消息确认情况。 AcknowledgeMode.NONE RabbitMQ成功…

Google高性能开源框架gRPC:快速搭建及HTTP/2抓包

一、什么是gRPC gRPC是google发起的一个*远程过程调用&#xff08;rpc&#xff09;*开源框架&#xff0c;可以在任何语言中&#xff0c;用任何编程语言编写。gRPC基于HTTP/2协议&#xff0c;使用Protocol Buffers作为序列化工具。 gRPC官网&#xff1a;https://grpc.io/ RPC …

目标跟踪:Mobile Vision Transformer-based Visual Object Tracking

论文作者&#xff1a;Goutam Yelluru Gopal,Maria A. Amer 作者单位&#xff1a;Concordia University 论文链接&#xff1a;https://arxiv.org/pdf/2309.05829v1.pdf 项目链接&#xff1a;https://github.com/goutamyg/MVT 内容简介&#xff1a; 1&#xff09;方向&#…

使用凌鲨调试网络接口

接口或API是程序员之间进行沟通和协作的重要工具之一。通过接口或API&#xff0c;程序员可以相互调用和共享代码、数据和资源&#xff0c;从而提高协作和开发的效率与便捷性。接口调试的难易程度直接决定了协作的效率。 凌鲨支持主流的接口协议&#xff0c;包括GRPC、OPENAPI/…

Windows PostgreSql 创建多个数据库目录

1 使用默认用户Administrator 1.1初始化数据库目录 E:\Program Files\PostgreSQL\13> .\bin\initdb -D G:\DATA\pgsql\data3 -W -A md5 1.2连接数据库 这时User为Administrator&#xff0c;密码就是你刚才设置的&#xff0c;我设置的为123456&#xff0c;方便测试。 2 添加…

算法leetcode|81. 搜索旋转排序数组 II(rust重拳出击)

文章目录 81. 搜索旋转排序数组 II&#xff1a;样例 1&#xff1a;样例 2&#xff1a;提示&#xff1a;进阶&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 81. 搜索旋转排序数组 II&#xff1…

读取txt文件中的字符串内容并转换成tensor

import os import torch import numpy as np import json# 初始化数据集 dataset ""# 遍历文件夹下的所有文件 folder_path H:/学习资料/代码/python/jupyterlab_project/pytorch/log/ for file_name in os.listdir(folder_path):file_path os.path.join(folder_p…

【计算机网络】网络入门基础

文章目录 1. 网络发展历史2. 认识协议OSI七层协议 3. TCP/IP协议网络与操作系统 4. 数据传输流程4.1 数据包的封装和分用4.2 同一局域网两台主机的通信4.3 跨一个路由器的两个局域网的通信 5. 网络中的地址管理IP地址MAC地址 1. 网络发展历史 &#x1f4a8;计算机网络的发展历…

C/C++数1的个数 2019年9月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C数1的个数 一、题目要求 1、编程实现 2、输入输出 二、解题思路 1、案例分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 C/C数1的个数 2019年9月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 给定一个十进制正整数n&#xff0c;写下…

奇点云:企业级数据基础设施的设计思路是“操作系统”

「数据场景复杂多变&#xff0c;只能不断推倒重构&#xff1f;」 近日&#xff0c;在《数据云场景指南》线上发布会&#xff0c;“数据云操作系统”同期亮相。奇点云合伙人、CTO地雷谈到&#xff0c;企业级数据基础设施应采用“操作系统”的设计&#xff0c;来帮助企业应对多云…

uniapp 模糊搜索(小白必看)

实现模糊搜索很简单,按照下面的步骤: 1. 搜索栏 <view class"search-box"><uni-search-bar class"uni-mt-10" radius"100" placeholder"请输入移交信息" clearButton"auto" bgColor"#F8F8F8"cancelBut…

安卓核心板开发板的操作系统版本有哪些?

安卓核心板也就是安卓主板平台&#xff0c;一般是指芯片主板硬件平台以及系统开发平台的总称。采用ARM应用处理器平台运行google的android智能化操作系统&#xff0c;是将核心功能封装的一块电子主板&#xff0c;集成芯片、存储器和功放器件等&#xff0c;并提供标准接口的芯片…