单调栈(续)、由斐波那契数列讲述矩阵快速降幂技巧

在这里先接上一篇文章单调栈,这里还有单调栈的一道题

题目一(单调栈续)

给定一个数组arr, 返回所有子数组最小值的累加和

就是一个数组,有很多的子数组,每个数组肯定有一个最小值,要把所有子数组的最小值的累加和。例如数组 [3 1 4 2] 子数组 3 的最小值是 3 子数组 3 1 的最小值是  1  子数组 3  1  4 的累加和是1 等等,把所有的累加和相加,就是我们要求的。这一题我们暴力解肯定是不行的,因为时间复杂度太高了  ......   我们假设 i 位置的数是 x我们要找到以x  为最小值的子数组的范围,举个具体的例子, i =10位置的数是  7  ,我左边找离我最近的比我小的 值是 5 ,它在 5位置,右边离我最近的比我小的是 4 在 15 位置,中间这个位置是从哪到哪呢,是从 6 位置开始 到 14 位置结束,而且从 6 到 14  位置最小值是 这个7,我们看从6 到 14 这些数组中,有多少个是以7为最小值的,我们第一反应是都是,都是吗,我们假设 6位置是个10  我们 6 到 6位置就是以10 为最小值的,假设7位置是 8 那么 6 到 7 就是以 7位置的8 为最小值的,那到底有多少是以 10 位置的7 为最小值的呢,你只要跨过10位置的7,就都是以该位置为最小值的,比如 6到 10位置, 7到 10位置,10位置到11位置,等等,都是。 6 7 8 9 10 11 12 13 14 一共有多少个子数组呢,我们需要跨过 10 位置  6  7 8 9 10  5个数字  10 11 12 13 14 5个数字,所以一共有 5 * 5 = 25 个 。 我们推广一下,假如说   一个i 位置,它的值为 x  ,左边离他最近的比它小的位置是k位置 值是 y,右边离他最近的比它小的位置是 j  值是z,那么产生多少累加和,i 是到不了k 的,得从 k + 1 开始,那有多少个开始位置呢 i - (k + 1) + 1个位置, 一化简,i - k 个位置,有多少个结束位置呢  (j - i )个结束位置,他们想 乘,就是子数组的数量这一题我们先假设是无重复值的。看是不是相当的清晰,注意刚刚我说是先假设无重复值的,那有点人就说,那我就是有重复值咋办,有重复值,就麻烦了,如果有重复值,我们需要做一些改进  举个栗子  2 4 5 3 6 6 6 3 7 8 6 3 5 3 2 ,我们关注 3  我们 3位置的 3 左边正常找比它小的,右边到 7 位置的 3 时我们就停住,在算 7 位置的 3时,左边比他小的位置可以算到 0位置的2  但右边就只能算到11位置的3 前一个位置。

代码如下:

public static int sumSubarrayMins(int[] arr){int[] left = nearLeftEqualsLeft(arr);int[] right = nearRightEqualsRight(arr);long sum = 0;for (int i = 0; i < arr.length; i++) {long start = i - left[i];long end = right[i] - i;sum += arr[i] * start * end;sum %= 1000000007;}return (int)sum;}private static int[] nearLeftEqualsLeft(int[] arr) {int[] result = new int[arr.length];Stack<Integer> stack = new Stack<>();for (int i = 0; i < arr.length; i++) {while (!stack.isEmpty() && arr[i] <= arr[stack.peek()]){Integer pop = stack.pop();result[pop] = stack.isEmpty() ? -1 : stack.peek();}stack.push(i);}while (!stack.isEmpty()){Integer pop = stack.pop();result[pop] = stack.isEmpty() ? -1 : stack.peek();}return result;}private static int[] nearRightEqualsRight(int[] arr){int[] result = new int[arr.length];Stack<Integer> stack = new Stack<>();for (int i = 0; i < arr.length; i++) {while (!stack.isEmpty() && arr[i] <= arr[stack.peek()] ){Integer pop = stack.pop();result[pop] =  i;}stack.push(i);}while (!stack.isEmpty()){Integer pop = stack.pop();result[pop] = arr.length;}return result;}

这里面用的是栈,时间复杂度为O(n),对于这一题来说,单从时间复杂度来算,其实已经算是最优解了,但是,对于常数时间,其实我们是可以继续进行优化的。那就是用数组代替栈,因为数组的寻址是很快的,所以,如果想进一步提升,就需要用数组代替栈去优化常数时间了。这里就直接把代码给大家了,有兴趣的同学可以研究研究。效率提升也还是蛮大的。

public static int sumSubarrayMins(int[] arr) {int[] stack = new int[arr.length];int[] left = nearLessEqualLeft(arr, stack);int[] right = nearLessRight(arr, stack);long ans = 0;for (int i = 0; i < arr.length; i++) {long start = i - left[i];long end = right[i] - i;ans += start * end * (long) arr[i];ans %= 1000000007;}return (int) ans;}public static int[] nearLessEqualLeft(int[] arr, int[] stack) {int N = arr.length;int[] left = new int[N];int size = 0;for (int i = N - 1; i >= 0; i--) {while (size != 0 && arr[i] <= arr[stack[size - 1]]) {left[stack[--size]] = i;}stack[size++] = i;}while (size != 0) {left[stack[--size]] = -1;}return left;}public static int[] nearLessRight(int[] arr, int[] stack) {int N = arr.length;int[] right = new int[N];int size = 0;for (int i = 0; i < N; i++) {while (size != 0 && arr[stack[size - 1]] > arr[i]) {right[stack[--size]] = i;}stack[size++] = i;}while (size != 0) {right[stack[--size]] = N;}return right;}

题目二.斐波那契数列

斐波那契数列大家都知道,包括我们要求斐波那契数列第n项,相信大家也都知道,正常我们来求,一个一个算呗,1 1 2 3 5 8 12 ......    是一个O(n)的方法是最优解吗? 不是,不是最优解,难道还有比O(n)还好的方法嘛,有O(log N) 逆天了,哇,怎么可能呢,你怎么能通过log N解决呢,有可能,来自于哪里,注意,我们都知道斐波那契数列是 F(n) = F(n-1) + F(n - 2) 并且第一项是 1 第二项也是 1 的严格递推式。面对这种除了初始项 剩下项是严格的递推式都有log N的方法。不光是斐波那契数列,只要是严格递推式,都有logN的方法。那什么样的式子没有logN的方法呢,有条件转移的式子没有logN的方法,比如说有个 if  else  在这种条件下是一个式子,在另一种条件下又是另一种式子,这种的就没有logN的方法。  这里用到了一点的线性代数,但是它并不难,什么意思呢,就是这样的, F(N) = F(N-1) + F(N-2)的话,第N项和N-1项和N-2项是严格递推关系,那么来看,它减的最多的是谁,是 2 我们说他是一个2阶递推,2阶递推啥意思,我们知道斐波那契数列 F1 = 1, F2 = 1 ,它必存在下列关系。

\left | F3,F2 \right | = \left | F2,F1 \right | * \begin{vmatrix} a & b \\ c & d \end{vmatrix}

线性代数就是为了解决严格递推第几项的问题才发明的,所以不要问为什么,这是线性代数的思想基础。同样道理

\left | F4,F3 \right | = \left | F3,F2 \right | * \begin{vmatrix} a & b \\ c & d \end{vmatrix}

这个 2 * 2的矩阵到底是啥玩意呢,不知道,我们可以算出来。因为斐波那契数列前几项我们是可以列出来的对不对,来,我们来实际算一下。

                              

有啥用,我们继续往下看。

通过上图我们可以看到,我们如果想求斐波那契数列的第n项,关键点再求某个矩阵的某一个次方,只要你这个矩阵的某个次方算的足够快,我这个斐波那契数列第n项就算的足够快。对不对,那下面这个问题就来了,一个矩阵的n次方怎么算的足够快,我们先看一个特别特别基本的问题,一个数的n次方怎么算最快,比如 10^75 怎么算最快 我们把相同的方式用到矩阵上,算矩阵的n次方不就快了嘛。那10^75怎么算最快呢,如果我们一个一个乘,就需要乘74次,这样就是一个O(n)的方法。它不够快, 75次方的二进制是啥,我们看 75 怎么分解  75 = 64 + 8 + 2 + 1 所以 75的二进制就是 1001011,接下来我们看如下图。

我们先二进制为 1 的位置,代表需要一个 10 的对应的次方。数字的n次方解决了,那矩阵呢,也是一样的。我们看上图最开始 的 1 那么放在矩阵中应该是什么呢,单位矩阵,就是对角线都为 1 ,其余全为 0 的矩阵。

public class FibonacciProblem {public static int f1(int n) {if (n < 1) {return 0;}if (n == 1 || n == 2) {return 1;}return f1(n - 1) + f1(n - 2);}public static int f2(int n) {if (n < 1) {return 0;}if (n == 1 || n == 2) {return 1;}int res = 1;int pre = 1;int tmp = 0;for (int i = 3; i <= n; i++) {tmp = res;res = res + pre;pre = tmp;}return res;}// O(logN)public static int f3(int n) {if (n < 1) {return 0;}if (n == 1 || n == 2) {return 1;}// [ 1 ,1 ]// [ 1, 0 ]int[][] base = { { 1, 1 }, { 1, 0 } };int[][] res = matrixPower(base, n - 2);return res[0][0] + res[1][0];}public static int[][] matrixPower(int[][] m, int p) {int[][] res = new int[m.length][m[0].length];for (int i = 0; i < res.length; i++) {res[i][i] = 1;}// res = 矩阵中的1int[][] t = m;// 矩阵1次方for (; p != 0; p >>= 1) {if ((p & 1) != 0) {res = product(res, t);}t = product(t, t);}return res;}// 两个矩阵乘完之后的结果返回public static int[][] product(int[][] a, int[][] b) {int n = a.length;int m = b[0].length;int k = a[0].length; // a的列数同时也是b的行数int[][] ans = new int[n][m];for(int i = 0 ; i < n; i++) {for(int j = 0 ; j < m;j++) {for(int c = 0; c < k; c++) {ans[i][j] += a[i][c] * b[c][j];}}}return ans;}public static void main(String[] args) {int n = 19;System.out.println(f1(n));System.out.println(f2(n));System.out.println(f3(n));System.out.println("===");}}

题目三、返回n年后牛的数量

第一年农场有1只成熟的母牛A,往后的每年:
1)每一只成熟的母牛都会生一只母牛
2)每一只新出生的母牛都在出生的第三年成熟
3)每一只母牛永远不会死 返回N年后牛的数量

题目呢很好理解,然后大家观察下面的例子,是不是一下子就看出公式是什么了。

然后我们就开始推这个矩阵是什么

                                       

这样是不是就知道矩阵是什么了,看是不是和斐波那契数列一样,接下就知道该怎么办了吧

public class FibonacciProblem {public static int c1(int n) {if (n < 1) {return 0;}if (n == 1 || n == 2 || n == 3) {return n;}return c1(n - 1) + c1(n - 3);}public static int c2(int n) {if (n < 1) {return 0;}if (n == 1 || n == 2 || n == 3) {return n;}int res = 3;int pre = 2;int prepre = 1;int tmp1 = 0;int tmp2 = 0;for (int i = 4; i <= n; i++) {tmp1 = res;tmp2 = pre;res = res + prepre;pre = tmp1;prepre = tmp2;}return res;}public static int c3(int n) {if (n < 1) {return 0;}if (n == 1 || n == 2 || n == 3) {return n;}int[][] base = { { 1, 1, 0 }, { 0, 0, 1 }, { 1, 0, 0 } };int[][] res = matrixPower(base, n - 3);return 3 * res[0][0] + 2 * res[1][0] + res[2][0];}public static int[][] matrixPower(int[][] m, int p) {int[][] res = new int[m.length][m[0].length];for (int i = 0; i < res.length; i++) {res[i][i] = 1;}// res = 矩阵中的1int[][] t = m;// 矩阵1次方for (; p != 0; p >>= 1) {if ((p & 1) != 0) {res = product(res, t);}t = product(t, t);}return res;}// 两个矩阵乘完之后的结果返回public static int[][] product(int[][] a, int[][] b) {int n = a.length;int m = b[0].length;int k = a[0].length; // a的列数同时也是b的行数int[][] ans = new int[n][m];for(int i = 0 ; i < n; i++) {for(int j = 0 ; j < m;j++) {for(int c = 0; c < k; c++) {ans[i][j] += a[i][c] * b[c][j];}}}return ans;}public static void main(String[] args) {int n = 19;System.out.println(c1(n));System.out.println(c2(n));System.out.println(c3(n));System.out.println("===");}}

题目四、给定一个数N,想象只由0和1两种字符,组成的所有长度为N的字符串 如果某个字符串,任何0字符的左边都有1紧挨着,认为这个字符串达标 返回有多少达标的字符串

通过观察我们发现,这不就是 初始值为 1 和 2  的斐波那契数列嘛。代码也很简单,就直接给大家了。

public class FibonacciProblem {public static int[][] matrixPower(int[][] m, int p) {int[][] res = new int[m.length][m[0].length];for (int i = 0; i < res.length; i++) {res[i][i] = 1;}// res = 矩阵中的1int[][] t = m;// 矩阵1次方for (; p != 0; p >>= 1) {if ((p & 1) != 0) {res = product(res, t);}t = product(t, t);}return res;}// 两个矩阵乘完之后的结果返回public static int[][] product(int[][] a, int[][] b) {int n = a.length;int m = b[0].length;int k = a[0].length; // a的列数同时也是b的行数int[][] ans = new int[n][m];for(int i = 0 ; i < n; i++) {for(int j = 0 ; j < m;j++) {for(int c = 0; c < k; c++) {ans[i][j] += a[i][c] * b[c][j];}}}return ans;}public static int s1(int n) {if (n < 1) {return 0;}if (n == 1 || n == 2) {return n;}return s1(n - 1) + s1(n - 2);}public static int s2(int n) {if (n < 1) {return 0;}if (n == 1 || n == 2) {return n;}int res = 2;int pre = 1;int tmp = 0;for (int i = 3; i <= n; i++) {tmp = res;res = res + pre;pre = tmp;}return res;}public static int s3(int n) {if (n < 1) {return 0;}if (n == 1 || n == 2) {return n;}int[][] base = { { 1, 1 }, { 1, 0 } };int[][] res = matrixPower(base, n - 2);return 2 * res[0][0] + res[1][0];}public static void main(String[] args) {int n = 19;System.out.println(s1(n));System.out.println(s2(n));System.out.println(s3(n));System.out.println("===");}}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【stm32-新建工程】

stm32-新建工程 ■ 下载相关STM32Cube官方固件包&#xff08;F1&#xff0c;F4&#xff0c;F7&#xff0c;H7&#xff09;■ 1. ST官方搜索STM32Cube■ 2. 搜索 STM32Cube■ 3. 点击获取软件■ 4. 选择对应的版本下载■ 5. 输入账号信息■ 6. 出现下载弹框&#xff0c;等待下载…

MySQL 使用 MyFlash 快速恢复误删除、误修改数据

一、MyFlash MyFlash 是由美团点评公司技术工程部开发并维护的一个开源工具&#xff0c;主要用于MySQL数据库的DML操作的回滚。这个工具通过解析binlog日志&#xff0c;帮助用户高效、方便地进行数据恢复。MyFlash的优势在于它提供了更多的过滤选项&#xff0c;使得回滚操作变…

云计算在保险行业的应用:太平财险团财险理赔新核心业务系统案例

随着科技的快速发展&#xff0c;云计算技术已经成为推动保险行业数字化转型的重要力量。云计算为保险公司提供了弹性、可扩展的计算资源&#xff0c;使其能够灵活应对业务高峰和低谷&#xff0c;提高业务运营效率和风控水平。太平财险与太平金科联合开发的“团财险理赔新核心业…

LLVM 中 的 pass 及其管理机制

概述 LLVM 编译器框架的核心概念是任务调用和执行 编译器开发者将IR分解为不同的处理对象&#xff0c;并将其处理过程实现为单独的pass类型。在编译器初始化&#xff0c;pass被实例化&#xff0c;并被添加到pass管理中 pass 管理器(pass manager) 以流水线的方式将各个独立的…

HCS-华为云Stack-容器网络

HCS-华为云Stack-容器网络 容器隧道overlay VPC网络

vue3轮播图怎么做

先看效果 实现代码 <n-carouseleffect"card"dot-type"line"draggable:autoplay"!isHovered":current-index"currentIndex"prev-slide-style"transform: translateX(-150%) translateZ(-450px);opacity:1"next-slide-st…

结构设计模式 - 桥接设计模式 - JAVA

桥接设计模式 一. 介绍二. 桥接模式示例2.1 定义实现部分和具体实现2.2 定义抽象部分和细化抽象部分2.3 测试2.4 解释 三. 结论 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一.…

windows11 x64 23H2 企业纯净版2024.6.16

闲来无事试安装了下da_nao_yan的 【6月12日更新】Windows11 22631.3737企业版 23H2 自用优化版 &#xff08;原版地址&#xff1a;https://bbs.pcbeta.com/viewthread-1985546-1-1.html&#xff09;&#xff0c;感觉比原版流畅多了&#xff0c;重新按照自己习惯封装了下&#x…

使用 C# 进行面向对象编程:第 9 部分

使用 OOP 的用户活动日志 应用程序背后的关键概念 在这一部分中&#xff0c;我们将使用之前学到的一些 OOP 概念。我们将创建一个小型应用程序。在继续之前&#xff0c;请阅读我的文章user-activity-log-using-C-Sharp-with-sql-server/。在本课程中&#xff0c;我们将再次使…

LabVIEW进行负载测试

本文介绍了如何使用LabVIEW进行负载测试&#xff0c;通过一个具体案例详细讲解了测试系统的组成、工作原理和实现方法。系统采用先进的硬件和软件架构&#xff0c;结合LabVIEW的强大功能&#xff0c;成功实现了对设备的高效负载测试&#xff0c;确保了系统的可靠性和性能。 项…

拥抱开源,构建未来:王嘉树与 TDengine 的开源之旅

在当代的技术浪潮中&#xff0c;开源文化不仅催生了无数创新技术&#xff0c;也为广大技术爱好者提供了一个展示才华、相互学习的平台。我们今天采访到的这位北京邮电大学电子工程学院的研究生&#xff0c;就是在这样的背景下&#xff0c;通过开源活动不断探索、学习并实现自我…

2024年汉字小达人活动还有4个多月开赛:来做18道历年选择题备考吧

结合最近几年的活动安排&#xff0c;预计2024年第11届汉字小达人比赛还有4个多月就启动&#xff0c;那么孩子们如何利用这段时间有条不紊地准备汉字小达人比赛呢&#xff1f; 我的建议是充分利用即将到来的暑假&#xff1a;①把小学1-5年级的语文课本上的知识点熟悉&#xff0…

Docker 安装 MySQL5.7 和 MySQL8

文章目录 安装 MySQL5.7拉取镜像前期准备启动容器 安装MySQL8.0拉取镜像查看镜像前期准备启动容器 安装 MySQL5.7 拉取镜像 docker pull mysql:5.7拉下来镜像后 执行 docker images 此时我们已经有这个镜像了。 前期准备 在根目录下创建 app &#xff0c; 在 app 目录下创建…

蚂蚁SEO的蜘蛛对网页收录有帮助吗 ?

网页蜘蛛对网站收录有着至关重要的帮助。在深入探讨这一话题之前&#xff0c;我们首先需要了解网页蜘蛛的工作原理及其在互联网生态系统中的角色。 网页蜘蛛&#xff0c;也被称为网络爬虫或网络机器人&#xff0c;是搜索引擎的核心组成部分。它们按照特定的算法和规则&#xff…

力扣231. 2 的幂(位运算)

Problem: 231. 2 的幂 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.若为0和负数则直接返回false&#xff1b; 2.利用一个标志数mask令其为一&#xff0c;不断的算术左移同时和n做与&#xff08;&&#xff09;运算&#xff0c;统计n中二进制位为1的个数&a…

Java | Leetcode Java题解之第155题最小栈

题目&#xff1a; 题解&#xff1a; class MinStack {Deque<Integer> xStack;Deque<Integer> minStack;public MinStack() {xStack new LinkedList<Integer>();minStack new LinkedList<Integer>();minStack.push(Integer.MAX_VALUE);}public void …

windows环境下配置git环境变量

路由当前目录 vim 打开 .bash_profile PATH$PATH:/d/phpstudy_pro/Extensions/php/php7.4.3nts PATH$PATH:/d/phpstudy_pro/Extensions/php8/php8.2.9nts PATH$PATH:/d/phpstudy_pro/Extensions/composer2.5.8 …

搭建一个简单的深度神经网络

目录 一、引入所需要的库 二、制作数据集 三、搭建神经网络 四、训练网络 五、测试网络 本博客实验环境为jupyter 一、引入所需要的库 torch库是核心&#xff0c;其中torch.nn 提供了搭建网络所需的所有组件&#xff0c;nn即神经网络。matplotlib类似与matlab&#xff0…

【react小项目】bmi-calculator

bmi-calculator 目录 bmi-calculator初始化项目01大致布局01代码 02完善样式02代码 03输入信息模块03代码 04 使用图表04代码 05详细记录信息渲染05代码 06 让数据变成响应式的06-1输入框的数据处理06-2图表&#xff0c;和记录信息的区域数据处理 07 删除功能&#xff0c;撤销功…

弗洛伊德算法——C语言

弗洛伊德算法&#xff0c;是一种用于解决所有顶点对之间最短路径问题的经典算法&#xff0c;该算法通过动态规划的方法计算出从每个顶点到其他所有顶点的最短路径。 弗洛伊德算法的基本思想是逐步考虑每一个顶点作为中间点&#xff0c;更新所有顶点对之间的最短路径。它通过以…