最大子数列和的问题求解
来源:互联网 发布:java 写成绩划分 编辑:程序博客网 时间:2024/05/20 08:01
问题描述:求解连续子数列和最大。即给定一个序列a1,a2,…,an
求得ai,…,aj之和最大。ai,…,aj是连续的子数列。
数组a[0..n-1]
首先看最容易想到的:从下标i = 0,到下标 i = n-1
子数列就将从:
0 ~ n-1
1 ~ n-1
2 ~ n-1
…
n-1 ~ n-1
问题变成对这些序列的最大和的求解问题。
就是说从这些子数列中选择子数列,不是一定以最后一个元素作为结尾的子数列。这个写法可能会引起误导,特别说明。
设问题是从i~n-1,那么定义sum = 0, 并定义max = a[0];
循环从i到n-1,如果加上当前数字大于max,那么就更新max值否则不更新。
这样最终得到的最大的max便是要求的结果。
这是最简单但是复杂度很高的思路。
int maxsequence(vector<int> arr, int n) //O(n^2)算法{ int max = arr[0]; //初始化最大值为第一个元素 for(int i = 0; i < n; i++) { int sum = 0; for(int j = i; j < n; j++) { sum += arr[j]; if(sum > max) //如果加到一个正数也不一定可以更新max,因为前面可能加了好几个负数 { max = sum; } } } return max;}
这个双层循环,因此复杂度是
这个算法读起来会容易产生错误的感觉,实际上可以这样理解,比如i = 0时,子序列从第一个元素开始,但是从哪里结束是不定的。注意到sum是从0到后面的每一个元素的加和。如果sum值大于max了,将max值更新。特别注意sum是在连续加和。一轮循环结束表示从0开始的子序列最大的max值是什么。
sum = a[0]
sum = a[0]+a[1]
sum = a[0]+a[1]+a[2]
sum = a[0]+a[1]+a[2]+a[3]
…
sum = a[0]+a[1]+a[2]+a[3]+…+a[n-1]
每一次加和都与当前max比较,看是否可以更新max.
那么这一轮结束,就知道从0开始的子数列和最大是什么了。
再计算从1开始的子序列最大可能达到的max值。
再计算从2开始…
…
从n-1开始到n-1的max值。
注意这里的max是同一个变量,因此max在动态变化。
最好的算法是
int maxsequence(vector<int> a, int n){ int maxSum, maxCur; maxSum = maxCur = a[0]; //初始化最大和为a[0] for (int i=1; i<n; i++) { if (maxCur <= 0)//这里其实是maxCur+a[i]<= a[i]化简的版本 { maxCur = a[i]; //如果前面位置最大连续子序列和小于等于0,则以当前位置i结尾的最大连续子序列和为a[i] } else { maxCur += a[i]; //如果前面位置最大连续子序列和大于0,则以当前位置i结尾的最大连续子序列和为它们两者之和 } if (maxCur > maxSum) { maxSum = maxCur; //更新最大连续子序列和 } } return maxSum;}
这其实是思考问题的另外一个角度:用增量的思路来看。
假设这个最大子数列从第一个元素开始,那么什么时候我们认为不可能是从第一个元素开始了呢?
首先,第一个元素是负数,第一感觉就告诉你,哦,这个不会是最大子数列开头。只有在中间的负数,你才会想,后面可能出现的正数,填补了负数带来的亏空。
OK,现在第一个数不是负数 ,那么就有资格做一下领头试试看。往后加,出现正数,当然大欢喜,继续走,如果是负数,心里紧张一下,算一算和如果变负数或者是0了,那么表示前面的都白费了,啥也没累积到,于是换个起点重新出发。
再用同样的思路去跟踪。
比如在加到a[i]时和为0或者是个负数了,马上断腕,重新设定起点为a[i],再重新求和,一直往后推进。每次更新完当前的最大值,就要和全局的最大值比较,看是不是可以更新它。
就是这样的过程,但是这个分析可能有错,下面更新。
2016.11.15 00:28 update: 重新思考后发现以前的认知是不完善的,首先一样,我们假设第一个数是起始最大值与当前最大值。从第二个元素往后面扫描过去。如果当前最大值maxCur加上a[i]后,变得比a[i]小了,即:maxCur + a[i] <= a[i]。
这个我们也分成两部分看:
- maxCur + a[i] < a[i]
- maxCur + a[i] = a[i]
第一种表示,前面的累计和没什么乱用,加上当前值居然比当前值小,那么正常情况下,你会做怎样的决策?好比说,一条路上连续分布着一些物品,要求你看到物品都必须捡起来,有些物品是有价值的,有些物品价值是负的,具体怎么个负法自由想象。当你看到第i件物品时,你必须得捡起来啊,所以捡起来加和计算一下当前的和,如果背包里所有的价值和比第i件物品价值小,你忍不住会想,哎?干嘛不把包里的东西全都扔了!只留第i件物品呢?另外,由这个式子我们知道,等同于说maxCur是个负值。而第i件物品呢,可以是正可以是负,如果是负的价值,则下一轮验证时会更新。
第二种,如果加上眼前的这个物品价值等于眼前的物品价值,当然可以选择两种策略,不管直接往后加,或者把其他的全部扔了,只要眼前这件物品。价值是一样的。其实也就是说maxCur = 0。
综上两种情况,在算法中常常写的是maxCur <= 0,其实不是直接反映问题的实质,是做了一次数学计算后的结果。
理解了这个关键点,整个算法的思路是非常简单易懂的。
此外,这个算法还可以标记子数组的起点,也就是更新maxCur = a[i]时,也可以更新起点就是i,用个容器全局存一下即可。而在更新max时,用一个全局容器存储当前的i也就是目前状况下的最大子数列的右端下标。继续扫描更新即可。
- 最大子数列和的问题求解
- c++ 求解数列最大的两个子段和
- 最大子序列和问题的求解
- 最大子序列和问题的求解
- 最大子序列和问题的求解
- 最大子数列和的问题
- #初学算法#分治策略---最大子数列的暴力求解和分治求解对比
- 子数列的最大和
- 带负数的数列,求解和最大的相邻子序列之一:性能最佳
- 带负数的数列,求解和最大的相邻子序列之二:代码清晰
- 带负数的数列,求解和最大的相邻子序列之三:穷举法
- 算法笔记1-最大子序列和问题的求解
- 求解最大子列和问题的四种算法
- 关于求解最大子序列和问题的总结
- 20160919求解最大子列和的问题
- java最大子序列和问题的求解
- 【数据结构与算法】最大子序列和问题的求解
- 求解最大子序列和问题的线性时间算法
- 学习Spring必学的Java基础知识
- 一个简单的对话框工具类
- 查看ubuntu的版本信息
- 主从复制报1173错误处理
- Qt之连接MySQL
- 最大子数列和的问题求解
- 最大密度闭合子图
- 1134 最长递增子序列
- MapReduce作业运行全貌
- 利用css和JSTL以及EL表达式生成颜色不一样的表格
- Codeforces #374 div2 C Dp
- 插入排序(升序)
- C++ strlen()+1问题
- struts2总结