当前位置: 首页 > 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 …...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...