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

简介Kadane算法及相关的普通动态规划

简介Kadane算法及相关的普通动态规划

本文详细论述Kadane算法的经典题目,并通过“首先列出动态规划解法,再改为Kadane算法解法”的方式,讲解二者的不同。最后给出一道Kadane算法变体的题目,解法极为简洁优美。

Kadane算法也是一种动态规划算法,它的时间复杂度也是O(n), 但它与普通的动态规划有什么不同呢?这里先给出结论,不同就是:它的空间复杂度是O(1),而普通动态规划的空间复杂度是O(n).

下面的例题-1是一道经典的可以用Kadane算法求解的题目。

例题-1 LeetCode-53

给定一个整数数组,求它的子数组之和的最大值。
比如,数组为[-2,1,-3,4,-1,2,1,-5,4], 则子数组元素之和最大为6,因为当子数组为 [4,-1,2,1]时可取得最大值。

解法-1. 普通动态规划法

众所周知,动态规划法需要有一个递推公式。这个公式的思考与解析如下。
假设有一个同样大小的dp数组,其第i个位置的元素dp[i]的含义是:原整数数组s从0至i处且包含元素i时的所有子数组中子数组元素之和的最大值。
听见来很拗口是不是。说白了就是,对于所有的以s[i]为最右元素的子数组求子数组元素之和,最大的那个值就是dp[i].
想一想,如果我们知道了所有的dp[0], dp[1], …,dp[size-1], 那么我们是不是就知道了子数组之和的最大值了呢。
这里要注意的是,当 i < j < size 时, dp[i] 有可能大于 dp[j].
知道 dp[i] 的定义后,那么 dp[i+1] 是什么呢?
很明显,dp[i] 与 dp[i+1] 的差异就在于原数组的第 i+1 个元素,即 s[i+1].
一种可能是:s[i+1]可以被继续累加到当前最大值上,即dp[i]和s[i+1]都是非负数,那么 dp[i+1] = dp[i] + s[i+1]
另一种可能是:s[i+1]不可以被继续累加到当前最大值上, 比如:dp[i]是负数,而s[i+1]又比dp[i]大, 于是只好从s[i+1]开始算,即 dp[i+1] = s[i+1]
综合这2个式子:dp[i+1] = max(dp[i]+s[i+1], s[i+1])
换一种写法: dp[i] = max(dp[i-1]+s[i], s[i])
在当前定义下,我们最后需要返回 dp 数组中的最大元素。

这里有一个问题,为什么不能把 dp[i] 定义为“从0至i处的所有子数组中子数组元素之和的最大值”呢?
如果可以这么定义的话,那么我们就不需要在dp数组中找一个最大值,而只要返回dp数组的最后一个元素即可了。
其实对于有些问题时完全可以这么做的,比如下面的例题-2.
但是对于例题-1来说,鉴于其所求是子数组元素之和的最大值,它相当于对之前的多个元素有依赖关系,如果那样定义的话,则无法建立dp[i+1]和dp[i]之间的递推关系。
这个问题是一个隐藏较深且不太容易解释的问题。喜欢对算法作深入思考的朋友可以多想想这里,看是否有更加简洁精辟的见解。

普通动态规划法的代码还是比较简洁的,如下:

class Solution {public int maxSubArray(int[] nums) {int [] dp = new int [nums.length];dp[0] = nums[0];int result = nums[0];for (int i=1; i<nums.length; i++) {dp[i] = Math.max(nums[i], nums[i]+dp[i-1]);result = Math.max(result, dp[i]);}return result;}
}

解法-2. Kadane算法

上面的整个dp数组是否必要呢?
其实不是必要的。公式中的 dp[i] 和 dp[i-1] 看似不同,但其实可以巧妙地用一个变量来代表,从而整个dp数组也就不需要了 – 只用一个变量来维护dp[i]. 所以Kadane算法的空间复杂度是O(1). 这个技巧还是很值得学习的。

代码如下:

class Solution {public int maxSubArray(int[] nums) {int max_here = nums[0];int result = nums[0];for (int i=1; i<nums.length; i++) {max_here = Math.max(nums[i], nums[i]+max_here);result = Math.max(result, max_here);}return result;}
}

例题-1中对于dp[i]的定义还是不那么符合人的直觉的,而下面例题-2对 dp[i] 的设定则非常直接了。

例题-2. LeetCode-121

给定一个整数数组,其中每一个数字代表了股票在当天的价格。你每天只能操作一次,这一次是买或者卖股票,请最大化利润。
比如:[7,1,5,3,6,4],最大化利润是5. 因为在价格为1的那天买,在价格为6的那天卖,可以得到5的利润。

解法-1. 普通动态规划法

有了前面那么难的题,这题就简单了。对于动态规划,就是主要要考虑递推公式。
设定一个同样大小的数组dp,那么很自然地就想到 dp[i] 就代表到当天为止的最大利润。
那么dp[i+1]是什么呢? 假设原数组叫prices, 此时相当于多了一个 prices[i+1], 所引起的变化就是也许 prices[i+1] 能得到更高利润。
怎么会得到更高利润呢?如果用prices[i+1]减去之前的最小值,则是它得到的利润值;如果这个值比dp[i]大,则有了更高利润。
由此可见,要记录一个历史最小值。这个并不难实现。
这样一分析,递推公式就出来了: dp[i+1] = max(dp[i], prices[i+1]-minValue)
由此也可见,我们最后需要返回的就是 dp[size-1],即dp数组的最后一个元素。

普通动态规划法代码如下:

int maxProfit(vector<int>& prices) {if (prices.empty()) return 0;vector<int> dp(prices.size());dp[0] = 0;int minPrice = prices[0];for (int i=1; i<prices.size(); i++) {dp[i] = max(dp[i-1], prices[i] - minPrice);if (prices[i] < minPrice) minPrice = prices[i];}return dp[prices.size()-1];
}

解法-2. Kadane算法

仍然对普通动态规划法稍作优化,用一个变量代替dp[i]和dp[i+1], 由此消除dp数组。
代码如下:

int maxProfit(vector<int>& prices) {if (prices.empty()) return 0;int max_here = 0;int minPrice = prices[0];for (int i=1; i<prices.size(); i++) {max_here = max(max_here, prices[i] - minPrice);if (prices[i] < minPrice) minPrice = prices[i];}return max_here;
}

除了上面2道例题,有的时候会出现 Kadane 算法的变体。这个时候需要因为一些特殊条件而做出一些巧妙的变化,则可以继续使用Kadane算法。

例题-3 LeetCode-152

给定一个整数数组,求其子数组的乘积最大值。
比如:[2,3,-2,4],其子数组乘积最大值为6,因为 2x3=6.

首先,还是要找出递推数组。假设dp[i]为“至位置i处的包含了位置i的子数组乘积最大值”,那么对于例子中给定的数组,对应的dp数组是这样的:[2, 6, -2, 4]
所以, dp[i+1] = max(dp[i]*s[i+1], s[i])
由此似乎可以写出程序了。和例题-1很像嘛!是不是直接套就可以了?
但注意这样的数组: [-2, 3, -4], 很明显答案是24,但应用上面算法的dp数组是 [-2, 3, 3].
原因很简单,每遇到负数一次则结果反转,也就是说,如果应用了上面的算法,则-2无法被后面再一次遇到负数时用上。
换句话说,尽管dp[1]为3是正确的,但dp[2]还是3就不正确了,而此时dp[0]或原数组nums[0]的信息无法被dp[2]用上。
至此,普通的动态规划似乎不再能够应用了,因为递推关系似乎无法推出来。怎么办呢?其实Kadane算法仍可以应用。
鉴于负数每乘一次都会反转结果,我们就只好一直乘,直至结尾。如果负数的个数是偶数,则这就是结果(先不考虑0)。
但如果负数的个数是奇数,比如1个,那我们就不知道是在这个负数的左边还是右边会出现乘积最大值了。
这又怎么办呢?也很好办。从左往右和从右往左分别扫一遍。因为我们不可能在一遇到负数的时候就重启计算。
最后,一旦遇到0怎么办呢?这就要重启计算了。相当于数字0将数组分段了。一旦遇到0,则从0后面的第1个数字开始,重启我们的乘法运算,直至下一个0或数组在当前方向上结束。
啰里吧嗦说了很多,但代码其实还是很简洁的。

class Solution {public int maxProduct(int[] nums) {long r1 = Integer.MIN_VALUE;long p1 = 1;for (int i=0; i<nums.length; i++) {p1 = p1 * nums[i];if (p1 > r1) r1 = p1;if (p1 == 0) p1 = 1;}long r2 = Integer.MIN_VALUE;long p2 = 1;for (int i=nums.length-1; i>=0; i--) {p2 = p2 * nums[i];if (p2 > r2) r2 = p2;if (p2 == 0) p2 = 1;}return (int)(r1>r2?r1:r2);}
}

由本题可见,Kadane算法其实可能比普通动态规划更加灵活。

(END)

相关文章:

简介Kadane算法及相关的普通动态规划

简介Kadane算法及相关的普通动态规划 本文详细论述Kadane算法的经典题目&#xff0c;并通过“首先列出动态规划解法&#xff0c;再改为Kadane算法解法”的方式&#xff0c;讲解二者的不同。最后给出一道Kadane算法变体的题目&#xff0c;解法极为简洁优美。 Kadane算法也是一…...

校园教务管理系统

学年论文&#xff08;课程设计&#xff09; 题目&#xff1a; 信息管理系统 校园教务管理系统 摘要&#xff1a;数据库技术是现代信息科学与技术的重要组成部分&#xff0c;是计算机数据处理与信息管理系统的核心&#xff0c;随着计算机技术的发展&#xff0c;数据库技…...

【LeetCode热题100】【双指针】接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] …...

软件工程-(可行性分析、需求分析)

目录 一.可行性研究 1.1定义 1.2项目背景 1.3三方面研究目标系统的可行性 1.3.1技术可行性分析 1.3.2 经济可行性分析 1.3.3 市场可行性分析 1.4. 数据流图 数据字典&#xff08;DD&#xff09; 1.5风险评估 1.6结论与建议 二、需求分析 引言 项目概述 利益相关者分析…...

HuggingFace学习笔记--BitFit高效微调

1--BitFit高效微调 BitFit&#xff0c;全称是 bias-term fine-tuning&#xff0c;其高效微调只去微调带有 bias 的参数&#xff0c;其余参数全部固定&#xff1b; 2--实例代码 from datasets import load_from_disk from transformers import AutoTokenizer, AutoModelForCaus…...

阅读笔记|A Survey of Large Language Models

阅读笔记 模型选择&#xff1a;是否一定要选择参数量巨大的模型&#xff1f;如果需要更好的泛化能力&#xff0c;用于处理非单一的任务&#xff0c;例如对话&#xff0c;则可用选更大的模型&#xff1b;而对于单一明确的任务&#xff0c;则不一定越大越好&#xff0c;参数小一…...

JSP 设置静态文件资源访问路径

这里 我们先在 WEB目录webapp 下创建一个包 叫 static 就用它来存静态资源 然后 我们扔一张图片进去 我们直接这样写 如下图 找到父级目录 然后寻找下面的 static 下的 img.png 运行代码 很明显 它没有找到 这边 我们直接找到 webapp目录下的 WEB-INF目录下的 web.xml 加入…...

【Pytorch】Visualization of Feature Maps(4)——Saliency Maps

学习参考来自 Saliency Maps的原理与简单实现(使用Pytorch实现)https://github.com/wmn7/ML_Practice/tree/master/2019_07_08/Saliency%20Maps Saliency Maps 原理 《Deep Inside Convolutional Networks: Visualising Image Classification Models and Saliency Maps》&…...

java第三十课

电商项目&#xff08;前台&#xff09;&#xff1a; 登录接口 注册接口后台&#xff1a; 注册审核&#xff1a;建一个线程类 注意程序中的一个问题。 这里是 5 条记录&#xff0c;2 条记录显示应该是 3 页&#xff0c;实际操作过程 有审核机制&#xff0c;出现了数据记录动态变…...

Scala--2

package scala02object Scala07_typeCast {def main(args: Array[String]): Unit {// TODO 隐式转换// 自动转换val b: Byte 10var i: Int b 10val l: Long b 10 100Lval fl: Float b 10 100L 10.5fval d: Double b 10 100L 10.5f 20.00println(d.getClass…...

【SQL SERVER】定时任务

oracle是定时JOB&#xff0c;sqlserver是创建作业&#xff0c;通过sqlserver代理实现 先看SQL SERVER代理得服务有没有开 选择计算机右键——>管理——>服务与应用程序——>服务——>SQL server 代理 然后把SQL server 代理&#xff08;MSSQLSERVER&#xff09;启…...

MyBatis-Plus学习笔记(无脑cv即可)

1.MyBatis-Plus 1.1特性 无侵入&#xff1a;只做增强不做改变&#xff0c;引入它不会对现有工程产生影响&#xff0c;如丝般顺滑损耗小&#xff1a;启动即会自动注入基本 CURD&#xff0c;性能基本无损耗&#xff0c;直接面向对象操作强大的 CRUD 操作&#xff1a;内置通用 M…...

【VUE】watch 监听失效

如果你遇见了这个问题&#xff0c;那么尝试在 watch 函数中设置 { deep: true } 选项。这告诉 Vue 监听对象或数组内部的变化&#xff0c;就像下面这样&#xff1a; watch(()>chatStore.dataSources,(oldValue, newValue)>{// 监听执行逻辑 }, { deep: true })嗯&#x…...

python的异常处理批量执行网络设备的巡检命令

前言 在网络设备数量超过千台甚至上万台的大型企业网中&#xff0c;难免会遇到某些设备的管理IP地址不通&#xff0c;SSH连接失败的情况&#xff0c;设备数量越多&#xff0c;这种情况发生的概率越高。 这个时候如果你想用python批量配置所有的设备&#xff0c;就一定要注意这…...

react native 环境准备

一、必备安装 1、安装node 注意 Node 的版本应大于等于 16&#xff0c;安装完 Node 后建议设置 npm 镜像&#xff08;淘宝源&#xff09;以加速后面的过程&#xff08;或使用科学上网工具&#xff09;。 node下载地址&#xff1a;Download | Node.js设置淘宝源 npm config s…...

PGSQL(PostgreSQL)数据库安装教程

安装包下载 下载地址 下载后点击exe安装包 设置的data存储路径 设置密码 设置端口 安装完毕&#xff0c;配置PGSQL的ip远程连接&#xff0c;pg_hba.conf&#xff0c;postgresql.conf&#xff0c;需要更改这两个文件 pg_hba.conf 最后增加一行 host all all …...

识别和修复网站上损坏链接的最佳实践

如果您有一个网站&#xff0c;我们知道您花了很多时间在它上面&#xff0c;以使其成为最好的资源。如果你的链接不起作用&#xff0c;你的努力可能是徒劳的。您网站上的断开链接可能会以两种方式损害您的业务&#xff1a; 它们对企业来说是可怕的&#xff0c;因为当消费者点击…...

使用Navicat连接MySQL出现的一些错误

目录 一、错误一&#xff1a;防火墙未关闭 二、错误二&#xff1a;安全组问题 三、错误三&#xff1a;MySQL密码的加密方式 四、错误四&#xff1a;修改my.cnf配置文件 一、错误一&#xff1a;防火墙未关闭 #查看防火墙状态 firewall-cmd --state#关闭防…...

4G基站BBU、RRU、核心网设备

目录 前言 基站 核心网 信号传输 前言 移动运营商在建设4G基站的时候&#xff0c;除了建设一座铁塔之外&#xff0c;更重要的是建设搭载铁塔之上的移动通信设备&#xff0c;这篇博客主要介绍BBU&#xff0c;RRU以及机房的核心网等设备。 基站 一个基站有BBU&#xff0c;…...

iphone/安卓手机如何使用burp抓包

iphone 1. 电脑 ipconfig /all 获取电脑网卡ip&#xff1a; 192.168.31.10 2. 电脑burp上面打开设置&#xff0c;proxy&#xff0c;增加一条 192.168.31.10:8080 3. 4. 手机进入设置 -> Wi-Fi -> 找到HTTP代理选项&#xff0c;选择手动&#xff0c;192.168.31.10:8080 …...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...