最优子序列问题: 算法
给定一个序列,求和最大的连续子序列(由于序列中会有负值存在)。 优化
一些朴素算法就再也不说了,仍是说说DP。 spa
考虑待查序列a[1...n],定义b[i]为序列a[1...i]的最大后缀,因而乎有了以下动归方程: 原理
b[i]=max{ b[i-1]+a[i] , a[i] } 循环
为何呢?针对b[i-1]考虑仅有的两种状况:1).若b[i-1]>0,那么a[1...i]的最大后缀必然是a[i]+b[i-1]。2).若b[i-1]<=0,固然a[1...i]的最大后缀就是a[i]了! 动态规划
如今考虑b[1...n],由于b[i]是a[1...i]的最大后缀的和,那么a[1...n]的最大子序列必然是b[i]中的一个(由于最大子序列必然是a[1...n]某个前缀串的后缀串),显然是b[i]中的最大值! 时间
接下来就是一次扫描计算b[1...n],而后找出最大的就OK了! co
原理很简单,不过这个DP的动态转移方程……为何动态规划里的状态转移方程老是那么神奇???!!! 思维
代码以下: 压缩
b[0]=a[0];
for(i=1;i<n;i++)
{
if(b[i-1]>0) b[i]=b[i-1]+a[i];
else b[i]=a[i];
}
for(i=1;i<n;i++)
if(b[i]>b[0]) b[0]=b[i];
cout<<b[0]<<endl ;
显然,能够作一个优化!
每一次循环迭代计算b[i],咱们只须要知道前一次迭代b[i-1]的值,因此不必存b[1...n]的全部值。每一次计算b[i],只须要根据前一次的迭代值pre计算本次迭代的值,时刻更新最大值!
代码以下:
max=0; pre=0;
for(i=0;i<n;i++)
{
if(pre>=0) pre+=a[i];
else pre=a[i];
if(pre>max) max=pre;
}
cout<<max<<endl;
还有一种迭代方式,这个稍微须要转换一下思惟。pre记录当前扫描位置a[i]对应的a[1...n]的前缀a[1...i]最长的非负后缀的和,显然若本次迭代后pre非负,那么就能够做为下一次扫描的前缀!若本次迭代后pre<0,那么pre置零(至关于从新开始记录一个序列)。
由于最终的最大子序列必然没有一个负前缀(不然能够去掉这个前缀以得到更大的子序列),那么a[1...n]的最大子序列必然就会出如今某次pre中(由于a[1...n]的最大子序列必然是迭代过程当中的一个非负前缀),每次迭代更新最大值保存最大子序列之和。
代码以下:
max=0;pre=0;
for(i=0;i<n;i++)
{
pre+=a[i];
if(pre>max) max=pre;
if(pre<0) pre=0;
}
cout<<max<<endl;
搞完最大子序列,又想到最大子矩阵,不过最大子矩阵就略微苦逼一点。迭代i , j把第 i~j 行压缩至一行,而后利用最大子序列来计算,时间复杂度O(n^3)……
目前貌似没有一个比较好的算法,值得研究一下!