神奇的動歸狀態轉移方程——最優子序列

最優子序列問題: 算法

給定一個序列,求和最大的連續子序列(由於序列中會有負值存在)。 優化

一些樸素算法就再也不說了,仍是說說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)……

目前貌似沒有一個比較好的算法,值得研究一下!

相關文章
相關標籤/搜索