3.4动态规划--最大字段和

news2025/4/10 19:29:48

要好好学习这个难受难受超级难受的动态规划了,千万不要再沉迷在看剧和玩耍里面了。必须承认最近没有好好学习。

写在前面

最大字段和书上介绍了三种解法:暴力、递归分治、动态规划

递归分治,一分为二,合并的时候有三种情况,注意考虑清楚

动态规划,最优解的数组b[j]表示以数字a[j]为结尾的最大字段和。然后递推方程就是根据题目要求,什么时候,能根据前面的已知结果找到新的最大字段和。由上一状态推导到当前状态,有什么条件??方法是什么??

问题描述

给定n个整数(可能有负数)组成的序列a1,a2,...,an,求该序列的最大子段和,就是对于形如\sum_{k=i}^{j}a_k的子段和的最大值。如果所有整数都是负数,那么定义其最大子段和为0。

例如:(-2,11,-4,13,-5,-2)最大字段和是20,11+(-4)+13=20

问题分析

暴力解法

其实这个问题也可以用暴力方法求解,它的时间复杂度是O(n^2)

用数组a[]记录原始输入的n个元素,然后暴力循环(第一个循环i记录开始的位置,第二个循环j记录加多少个元素)遍历所有的情况,更新最优值

分治方法

分治法解题的一般步骤:

  (1)分解,将要解决的问题划分成若干规模较小的同类问题;

  (2)求解,当子问题划分得足够小时,用较简单的方法解决;

  (3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。

序列a[1:n]可以分成长度相等的两段,a[1:n/2] a[n/2+1:n]

分别求出这两段的最大字段和,则合并的时候有三种情况:

A、a[1:n]的最大子段和与a[1:n/2]的最大子段和相同;

B、a[1:n]的最大子段和与a[n/2+1:n]的最大子段和相同;

C、a[1:n]的最大子段和在a[1:n/2]和a[n/2+1:n]各个去一小段拼接产生。

A、B这两种情形可递归求得。对于情形C,容易看出,a[n/2]与a[n/2+1]在最优子序列中。因此,我们可以在a[1:n/2]和a[n/2+1:n]中分别计算出如下的s1和s2。S1是子段a[1:n/2]从后往前加的最大值(包含a[n/2]),S2是子段a[n/2+1:n]从前往后加的最大值(包含a[n/2+1]),则s1+s2即为出现情形C使得最优值。

伪代码如下:

int MaxSubSum(int a, int left, int right){ 
int sum=0;
if (left==right) sum= a[left]>0?  a[left]:0;
else{
    int center=(left+right)/2; //计算中间值
//第一种情况算出来的leftsum
    int leftsum=MaxSubSum(a,left,center); 
//第二种情况算出来的rightsum
    int rightsum=MaxSubSum(a,center+1,right);
//算第三种情况的sum
    int s1=0;lefts=0;
//s1从后往前加,因为要包含a[n/2]
    for (int i=center;i>=left;i--){ 
        lefts+=a[i];
        if (lefts>s1) s1=lefts;
    }
    int s2=0;rights=0;
//s2从前往后加,因为要包含a[n/2+1]
    for (int i=center+1;i<=right;i++){ 
        rights+=a[i];
        if (rights>s2) s2=rights;
    }
//中间特殊段的sum
    sum=s1+s2;
    if (sum<leftsum) sum=leftsum;
    if (sum<rightsum) sum=rightsum;
  }
  return sum;
}

这个算法是典型的拆分成两个子问题,然后合并花费O(n)时间的问题

所以递归方程为:T(n)=\left\{\begin{matrix} O(1) \\2T(n/2) +O(n) \end{matrix}\right. ,解这个递归方程(主定理)得,T(n)=O(nlogn)

动态规划方法

已知前n个数的最大子段和,那么前n+1个数的最大字段和有两种情况,一是包含前面的结果,二是不包含。序列a[]有n个数,我们就要做n次决策,从第一个数开始(下标从1开始),假设已经做好了前 i 个数的决策,并把做第 i 个数的最大子段和的结果保存到了b[i](注意,前i个数的最大子段和sum和第i个数决策的子段和b[i]是不一样的,前者sum可能不包含第i个数,但第i个数决策的子段和b[i]一定包含b[i],sum是当前最大子段的和,而b[i]是包含第i个数的子段和,并想办法使b[i]的值尽可能的大),当做第i+1个数的决策时,要做的工作就只是判断包含第i+1个数的子段和是否要把tem的值包进来,如果tem>0,就包括,否则不包括。
再看一下总的想法)假设前n个数的最大子段和是b[n],在决策前n+1个数的最大子段和时,判断b[n]的值,如果b[n]>0,那么前n+1个数的最大子段和为b[n]加上第n+1个数,否则就是第n+1个数自己。这里记住,你所求的是连续的几个数的和。

b[j]的定义,b[j]是指以a[j]结尾的最大子段和。因此有如下公式:

b[j] = max{b[j - 1] + a[j] , a[j]}      1=<j<=n

当bj-1>0时bj=bj-1+aj,否则bj=aj。由此可得计算bj的动态规划递归式bj=max{bj-1+aj,aj},1≤j≤n。时间复杂度为O(n)

从递推公式我们可以写出最大字段和的动态规划算法,伪代码如下:

int MaxSum(int n, int *a){
\\bj[]的定义是以aj数字结尾的最大字段和
    int sum=0; b=0;
    for (i=1;i<=n;i++){
\\b就是bj,如果前面的数字都>0,就一直加,如果有<0的数字,就放弃前面一段,重新开始算
        if (b>0) b+=a[i]; else b=a[i];
\\每一次都判断,更新最大值
        if (b>sum) sum=b;
    }
    return sum;
}

 代码如下:(精简版)

#include <iostream>
using namespace std;
const int maxN = 2e5 + 5;
int a[maxN];
int main() {
	int N, ans = -10000; cin >> N;
	for (int i = 1; i <= N; i++) {
		cin >> a[i];
		if (a[i - 1] > 0)
			a[i] += a[i - 1];
		ans = max(a[i], ans);
	}
	cout << ans;
}

代码如下:(思路清晰版)

#include <iostream>
using namespace std;
#define MAXN 30005
int a[MAXN];
int dp[MAXN];
int Max=0;
int maxsum(int n,int *a){
    dp[0]=0;
	for(int i=1;i<=n;++i)
	{
	    dp[i]=max(dp[i-1]+a[i],a[i]);
         //在i位置的情况可以分为:继承前面的所有数的总和+当前节点的和 或 仅选择当前节点
	    Max=max(Max,dp[i]);
	}
	return Max;
}
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    int res=maxsum(n,a);
    cout << "res= " << res << endl;
    return 0;
}

相关题目:

最大子段和 - 洛谷

双子序列最大和 - 洛谷

给定一个长度为n的整数序列,要求从中选出两个连续子序列,使得这两个连续子序列的序列和之和最大,最终只需输出最大和。一个连续子序列的和为该子序列中所有数之和。每个连续子序列的最小长度为1,并且两个连续子序列之间至少间隔一个数。

输入输出样例

输入 

5
83 223 -13 1331 -935

输出 1637

输入 #2

3
83 223 -13

输出  70

大致思路:题目是求连续两段的最大字段和,那么我们就可以从 1到n枚举 ,再从 n 到 1枚举,把每一段的最大字段和先标记一遍。

	for(ll i=1 ;i <= n; i++)
	{
		 scanf("%lld",&a[i]);
		 l[i]=max(l[i-1]+a[i],a[i]);
		 lf[i]=max(lf[i-1],l[i]);
	}
	for(ll i=n;i>=1;i--)
	{
		r[i]=max(r[i+1]+a[i],a[i]);
		rf[i]=max(rf[i+1],r[i]);
	}

由于求的是连续最大字段和,所以答案一定有一个分界点,那么我们接下来的任务就是找到这个分界点。所以我们从头到尾扫一遍 , 找出临界点,它左端的最大字段和,加上右端的最大字段和,即为答案。

	for(ll i=2;i<n;i++)
	{
		ans=max(lf[i-1]+rf[i+1],ans);
	}

 AC代码:

#include <bits/stdc++.h>
using namespace std;
int n,a[1000010],head[1000010],tail[1000010],sum1[1000010],sum2[1000010],ans;
int main(){
	cin >> n;
    \\一定要记得初始化
    sum1[0]=sum2[n+1]=ans=-9999999;
	for(int i=1;i<=n;i++) cin >> a[i];
	//从前往后找最大子段和 
	for(int i=1;i<=n;i++){ 
        head[i]=max(head[i-1]+a[i],a[i]);
        sum1[i]=max(head[i],sum1[i-1]);
    }
	//从后往前找最大子段和 
	for(int i=n;i>=1;i--){
        tail[i]=max(tail[i+1]+a[i],a[i]);
        sum2[i]=max(sum2[i+1],tail[i]);
    }
	//枚举断点 
	for(int i=2;i<n;i++) ans=max(ans,sum1[i-1]+sum2[i+1]);
	cout << ans;
	return 0;
}

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

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

相关文章

java容器轻松理解 collection collections(异同篇)

(1)collectionset &#xff08;只能迭代&#xff0c;不可以按下标取值&#xff09;hashset、Treesetlist&#xff08;可以迭代&#xff0c;也可以按下标取值&#xff09;ArrayList、LinkedList、Vector特点&#xff1a;ArrayList:方便随机访问&#xff0c;由数组实现的。所以中…

十大经典排序算法(动态演示+代码)-选择排序与插入排序

选择排序 一、什么是选择排序&#xff1f; 选择排序&#xff08;Selection sort&#xff09;是一种简单直观的排序算法。它的工作原理是&#xff1a;第一次从待排序的中数据元素选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c…

行为型模式-策略模式

1.概述 先看下面的图片&#xff0c;我们去旅游选择出行模式有很多种&#xff0c;可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。 作为一个程序猿&#xff0c;开发需要选择一款开发工具&#xff0c;当然可以进行代码开发的工具有很多&#xff0c;可以选择Idea进行开发&a…

04_iic子系统

总结 iic_client和iic_driver 加入iic总线的思想和paltform总线的玩法一样 把iic设备和驱动注册到iic总线中 构造出字符设备驱动和设备节点供app进行操作 但是iic硬件设备是挂在iic控制器下面的 所以iic控制器也会有自己的驱动和设备树节点 厂家一般都会帮做好 我们写的iic_dr…

离散系统的数字PID控制仿真-2

设计离散PID控制器&#xff0c;各信号的跟踪结果如图所示&#xff0c;其中S代表输入指令信号的类型。通过取余指令 mod实现三角波和锯齿波。当S1时为三角波&#xff0c;S2时为锯齿波&#xff0c;S3时为随机信号。在仿真过程中&#xff0c;如果 D1&#xff0c;则通过 pause命令实…

Prometheus学习整理-Prometheus-operator

Prometheus中的promQL语句: Prometheus提供的一种promQL语法,用来处理接口数据,然后方便用户对数据进行处理加工,它是Prometheus专门提供的一个函数表达式语言,可以实时的查询和聚合时间序列的数据,通过HTTPApi的方式提供给外部使用,PromQL主要分为下面的几种类型数据: 这里面的…

【老卫搞机】136期:华为开发者联盟社区2022年度战码先锋2期开源贡献之星

首先祝大家兔年大吉&#xff0c;身体安康&#xff0c;钱兔似锦&#xff01;接上次的“2022年牛人之星”&#xff08; https://developer.huawei.com/consumer/cn/forum/topic/0203109930647268095&#xff09;&#xff0c;今天咱们来开箱另外一件特殊的奖品&#xff0c;来自华为…

MySQL内外连接

文章目录MySQL内外连接内连接外连接左外连接右外连接简单案例MySQL内外连接 表的连接分为内连接和外连接。 内连接 内连接 内连接的SQL如下&#xff1a; SELECT ... FROM t1 INNER JOIN t2 ON 连接条件 [INNER JOIN t3 ON 连接条件] ... AND 其他条件;说明一下&#xff1a; …

零基础学JavaWeb开发(二十三)之 springmvc入门到精通(3)

5、springspringmvcmybatis整合 5.1、项目技术需求分析 1.使用ssmlayui技术开发 对用户表数据实现增删改查 采用前后端分离架构模式 5.2、SSM环境的整合之提供增删改查 整合数据库表结构 CREATE TABLE mayikt_users (id int NOT NULL AUTO_INCREMENT,name varchar(255) CH…

Mysql入门技能树-使用数据库

创建和删除数据库 Joe 在开发机上创建了一个名为 goods 的数据库&#xff0c;做了一些练习&#xff0c;现在他需要删除这个数据库&#xff0c;重建一个 goods。那么他需要的步骤是&#xff1a; 答案是&#xff1a;A 创建数据库的语法格式如下&#xff1a; CREATE DATABASE d…

client-go实战之九:手写一个kubernetes的controller

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 系列文章链接 client-go实战之一&#xff1a;准备工作client-go实战之二:RESTClientclient-go实战之三&#xff1a;Clientsetclient-go实战之四&#xff1a;…

Java IO流之字符集总结

ASCII字符集、GBK字符集、Unicode字符集 这里我直接上总结了&#xff0c;关于这三种字符集的基本介绍大家可以百度一下。 在计算机中&#xff0c;任意数据都是以二进制的形式来存储的计算机中最小的存储单元是一个字节ASCII字符集中&#xff0c;一个英文占一个字节简体中文版Wi…

CQF量化金融职业指南

✏️写作&#xff1a;个人博客&#xff0c;InfoQ&#xff0c;掘金&#xff0c;知乎&#xff0c;CSDN &#x1f4e7;公众号&#xff1a;进击的Matrix &#x1f6ab;特别声明&#xff1a;原创不易&#xff0c;未经授权不得转载或抄袭&#xff0c;如需转载可联系小编授权。 概述 …

二分查找算法的实现以及解决整数溢出问题

前言 从今天起我会开启一个专栏&#xff1a;Java面试八股文,记录一下我在网上学到的Java面试常考的一些内容&#xff0c;注意:本人暂无面试经验&#xff0c;只是在网上找视频学习到的❗❗❗ 二分查找 我们首先要学习的是二分查找&#xff0c;我相信很多人跟我一样&#xff0c;在…

MySQL - text字段

一、text属性 MySQL下的TEXT属性一种特殊的字符串&#xff0c;存储单位为字节&#xff0c;有四种类型 TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT 不同的是可以存储的字符串的长度以及空间占用大小 TINYTEXT最大存放长度为255个字符的字符串 TEXT最大存放长度为65535个字符的…

XlsReadWriteII EXCEL Cell 单元数据读写

XlsReadWriteII EXCEL Cell 单元数据读写 下面从编程的眼光&#xff0c;从XlsReadWriteII的角度&#xff0c;谈谈EXCEL的理解。回顾一下EXCEL发展史&#xff1a; 1982年&#xff1a;微软推出了它的第一款电子表格软件&#xff1a;Multiplan。 1985年&#xff1a;推出了…

Day06 C++STL入门基础知识三——String容器(下)比较-存取-插入-删除-子串获取【全面深度剖析+例题代码展示】

永远相信&#xff0c;美好的事情即将发生&#xff01; 文章目录1. 比较操作1.1 比较方式1.2 函数原型1.3 代码展示2. 字符读写2.1 字符读入/访问2.1.1 方式2.1.2 代码展示2.2 修改字符2.2.1 方式2.2.2 代码展示3. 插入和删除3.1 函数原型3.2 代码展示4. 截取子串(比较实用!!!)4…

蓝桥杯STM32G431RBT6学习——定时器输入捕获

蓝桥杯STM32G431RBT6学习——定时器输入捕获 前言 从省赛的题目来看&#xff0c;对于定时器输入捕获这块几乎不考&#xff0c;但是为了知识的完整性及避免万一&#xff0c;依旧有了解的必要。国信长天开发板上的定时器捕获主要针对于NE555波形发生器的方波进行频率、脉宽等测…

恶意代码分析实战 3 IDA Pro

利用IDA PRO分析Lab05-01.dll 实验目的 利用IDA Pro分析Lab05-01.dll中发现的恶意代码&#xff0c;回答以下问题&#xff1a; DLLMain的地址是什么&#xff1f; 可以空格转入反汇编查看DLLMain地址&#xff0c;或者 DLLMain的地址是.text:0x1000D02E。 使用Imports窗口并浏…

这几个步骤,让你的电脑避免卡顿~

C盘主要是系统运行的存储空间&#xff0c;如果C盘装满了东西&#xff0c;那就意味着电脑的存储空间小&#xff0c;电脑运行就会更慢。&#x1f62f; 另外&#xff0c;桌面上的文件也属于C盘&#xff0c;所以桌面也不要放太多东西。除装机时候的一些必要软件&#xff0c;后期装的…