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

这篇具有很好参考价值的文章主要介绍了3.4动态规划--最大字段和。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

写在前面

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

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

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

问题描述

给定n个整数(可能有负数)组成的序列a1,a2,...,an,求该序列的最大子段和,就是对于形如的子段和的最大值。如果所有整数都是负数,那么定义其最大子段和为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)=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代码:文章来源地址https://www.toymoban.com/news/detail-780742.html

#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;
}

到了这里,关于3.4动态规划--最大字段和的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法】Kadane‘s算法(动态规划、最大子数组和)

    Kadane\\\'s 算法是一种用于解决最大子数组和问题的动态规划算法。这类问题的目标是在给定整数数组中找到一个连续的子数组,使其元素之和最大(数组含有负数)。 算法的核心思想是通过迭代数组的每个元素,维护两个变量来跟踪局部最优解和全局最优解。 以下是Kadane’s算

    2024年03月22日
    浏览(102)
  • 【算法思考记录】动态规划入门!力扣2606. 找到最大开销的子字符串【Python3、动态规划】

    原题链接 动态规划(Dynamic Programming,简称 DP)是一种通过将原问题分解为相互重叠的子问题并只解决一次的方法来解决问题的算法优化技术。动态规划通常用于优化递归问题,通过存储子问题的解来避免重复计算,从而显著提高算法的效率。 动态规划的基本思想是将原问题

    2024年02月03日
    浏览(44)
  • 剑指offer(C++)-JZ47:礼物的最大价值(算法-动态规划)

    作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 题目描述: 在一个mtimes nm×n的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向

    2024年02月05日
    浏览(65)
  • 最大子矩阵(openjudge noi-2.6基本算法之动态规划-1768)

    来源 OpenJudge - 1768:最大子矩阵 题目描述 已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵。 比如,如下4 * 4的矩阵  0        -2        -7        0  9         2        -6        2 -4      

    2024年04月26日
    浏览(41)
  • 【算法题】动态规划中级阶段之跳跃游戏、最大子数组和、解码方法

    动态规划(Dynamic Programming,简称 DP)是一种解决多阶段决策过程最优化问题的方法。它是一种将复杂问题分解成重叠子问题的策略,通过维护每个子问题的最优解来推导出问题的最优解。 动态规划的主要思想是利用已求解的子问题的最优解来推导出更大问题的最优解,从而

    2024年02月11日
    浏览(51)
  • 【算法】Maximize Grid Happiness 最大化网格幸福感 动态规划

    给你四个整数 m、n、introvertsCount 和 extrovertsCount 。有一个 m x n 网格,和两种类型的人:内向的人和外向的人。总共有 introvertsCount 个内向的人和 extrovertsCount 个外向的人。 请你决定网格中应当居住多少人,并为每个人分配一个网格单元。 注意,不必 让所有人都生活在网格中

    2024年02月11日
    浏览(38)
  • 最大子段和——用蛮力算法,分治策略,动态规划算法三种求法(C语言)

    目录 一、题目 二、算法求解 1、蛮力算法 伪代码  算法分析 程序 2、分治策略 伪代码 算法分析 程序 3、动态规划算法 伪代码 算法分析 程序 设A=a1,a2,...,an是n个整数的序列,称ai,....,aj为该序列的连续子序列,其中1=i=j=n,子序列的元素之和称为A的子段和: 例如,A=-2,11,-4,1

    2024年01月24日
    浏览(55)
  • 算法 DAY52 动态规划10 1143.最长公共子序列 1035.不相交的线 53. 最大子数组和

    本题和动态规划:718. 最长重复子数组 (opens new window)区别在于这里不要求是连续的了 1、dp数组 dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j] 2、递推公式 因为不强调是连续的,当前dp[i][j] 就有三种路径可以选:dp[i-1][j] dp[i][j-1]

    2024年02月03日
    浏览(65)
  • 算法笔记(Java)——动态规划

    动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。 所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的, 动规和递归的区别是动规不是暴力的

    2024年02月08日
    浏览(49)
  • 算法笔记14.动态规划

    就是切割问题变成一堆小问题,最好子问题之间可以递推,这样就能利用之前的子问题的答案得出新的结果。 可以削减总量,比如求n个数的什么什么,可以削n的大小,n削到n-1……一直到1,1的结果很好求,然后利用1的结果求2,然后再一直求到n。 也可以不削总量削单量,比

    2024年04月15日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包