标签:
看书的时候看到一个问题,觉得挺有意思的,便想记一下。
问题是这样的,对于一个序列A1,A2,...AN,求使得ΣAk最大的值,其中 1≤ i ≤ k ≤ j ≤N。例如对于序列(-2,11,-4,13,-5,-2),其答案为20(从A2到A4)。
这本来是个很简单的问题,一种最直观的想法便是用循环,遍历所有可能的子序列的初始点和结束点,以下代码便可实现该功能,其中i为子序列初始点,j为子序列结束点。
int max_sum = 0; for(int i =0;i<seq.size();i++){ int this_sum = 0; for(int j =i;j<seq.size();j++){ this_sum += seq[j]; if(max_sum<this_sum){ max_sum = this_sum; } } }
在书中的部分,对于这个问题的讨论并不是如何实现,而是通过它告诉我们不同算法的复杂度。上述方法的复杂度为O(N2)主要是因为其中的两个for循环,那么是否有更小复杂度的方法呢?书中就告诉我们两个更小复杂度的方法(方法1比较经典但较为复杂,偷懒的同学可以直接看方法2)。
1.使用二分的思想,将原问题分解为考虑左半段子序列和最大,右半段子序列和最大,以及包含该序列中点值的序列和最大问题。这样就把长度为N的子序列和问题,转化为两个长度为N/2的子序列和问题,以及包含中点值的序列和最大值的问题。
如对子序列(-2,11,-4,13,-5,-2),则转化为(-2,11,-4),(13,-5,-2),以及包含(-4,13)点的序列和。该例子中左半段子序列最大值为11,右半段值为13。对于包含(-4,13)的序列,我们分别考虑以-4为结束点和13为开始点的序列和最大值,可知分别为7(11,-4),13,然后比较7,13,7+13从而知道包含(-4,13)的序列和最大值为7+13=20(11,-4,13)。最后比较三部分的序列值11,13,20可知包含(-4,13)的部分值最大为20。
该算法可使得算法复杂度降为O(Nlog2(N)),其中对转化的三部分中N/2的子序列和问题使用递归的方法调用原函数,而包含中点值的则通过固定初始点和结束点分别求得最大值后比较而可得出。该方法通过二分和递归的方法实现了该功能,并降低了算法复杂度,如果没有后面的方法2,这会是解决该问题一个很好的方案。因为该算法写出来的代码较长,而我比较懒,而且我们有更简单且有效的方法,就不在这给出该算法的代码了,有兴趣的同学可以自己去书中查看相应的代码。
2.最后介绍解决该问题的算法,算法复杂度仅为O(N),只需要遍历一次序列,便可实现,而且想法也十分简单!!
对于子序列问题我们主要考虑子序列的起始点和结束点。对于考虑子序列和最大的问题,若子序列和小于0,则该子序列则不可能是最优子序列的开头,因为总可以用后续的值加以改进。故若该子序列和小于0,则应将起始点设为下一个。若子序列和大于0,则不应改变子序列和,因为该子序列与添加的新值和应大于仅添加的新值。通过这样的扩展比较添加前后最大值而选择较大者作为结束点。例如对例子(-2,11,-4,13,-5,-2)进行分析,则(起始点,结束点,子序列和,最大值)随新值的添加依次为(0,0,0,0),-2(0,0,-2,0),11(2,2,11,11),-4(2,2,7,11),13(2,4,20,20),-5(2,4,15,20),-2(2,4,13,20)
我对书中代码稍微修改下(主要是增加找出了起始点和结束点的几行代码),放在下面。大家可以尝试验证下结论是否正确。
#include<iostream> #include<vector> using namespace std; void find_max_subseq(const vector<int> &seq){ int max_value=0,subseq_value=0,start_pos=0,end_pos=0; for(int i=0;i<seq.size();++i){ subseq_value +=seq[i]; if(subseq_value>max_value){ max_value=subseq_value;
start_pos = tmp_pos; end_pos = i+1; } else { if(subseq_value<0){ subseq_value = 0; if(i!=(seq.size()-1)){ tmp_pos =i+2; } } } } cout<<"max_value:"<<max_value<<endl; cout<<"start_pos:"<<start_pos<<endl; cout<<"end_pos:"<<end_pos; } int main(){ vector<int> seq; int length_seq; cout<<"输入序列长度"<<endl; cin>>length_seq; cout<<"输入序列值"<<endl; int num; for(int i=0;i<length_seq;++i){ cin>>num; seq.push_back(num); } find_max_subseq(seq); return 0; }
就这样我们就把算法复杂度从O(N2 )降低到了O(N),对于N值很大时,效果会有明显的改善。对于我们来说,更多的是学到一种思路吧,遇到问题可以多想想,虽然第一种方法很容易想到,不过如果多想想,能发现第三种方法的话是不是会更开心呢。
#读书笔记--数据结构与算法分析(C++版)最大子序列和问题。
标签:
原文地址:http://www.cnblogs.com/fypp/p/4783189.html