POJ 2479 最大子段和

2021-06-08 10:29:29 字數 3447 閱讀 1277

poj 2479 最大子段和

poj 2479嚴格來說不是單純的最大子段和,它是乙個雙向的最大子段和,為了弄清雙向的最大子段和就必須弄清楚單向的最大子序和。

單向最大子段和問題如下:

在序列a[1..n](

任意乙個ai都是整數,有正有負

)中找出乙個子串行a[p..q]使得m=ap+ap+1+…+aq-1+aq最大

例如如下的序列:

1 7 -8 -4 5 10 -9 -5

最大的一段是從和為15

為使m最大,則a[p]和a[q]必須為正數,(在q-p>=1情況下)

如果a[p]為負數,那麼為使m最大,可以捨棄a[p],只取a[p+1..q]的序列(乙個數加上乙個負數會變小,要使和最大不如不要這個負數)a[q]必須為正也是同樣的道理。

再推廣結論,子串行a[p..q]中a[p..k]的

子子串行

和也必須為正(p<=k<=q),因為a[p..k]的和可以看成乙個數,它是在最優子串行的最左邊的,若它是負數,那麼a[k+1..q]的序列和將更大,同樣地,a[k+1,.q]必須為正。

於是就有了這樣的想法:

最優子串行a[p..q]一定是在a[1..n]中的,它的左邊a[1..p-1]一定是負值或不存在,它的右邊a[q+1..n]也一定是負值或不存在。但滿足左邊子子串行是負的,右邊子子串行也是負的的子串行不一定是最優的,我可以比較所有這些序列的和,取其中的最大值。

於是設乙個變數i來從左向右遍歷序列,並分別求出a[1..i]中最大子串行的和sum,用max來記錄子串行的最大值。

開始在i累加的過程中,使sum累加a[i]

那麼就有了兩種情況:

1.累加過程中sum<0 例如 a[1]為負值(此時sum=a[1]),那麼a[1]不可能是最優子串行的左邊部分,如果它是最優子串行的左邊部分,那麼它一定大於零,這裡產生了矛盾,此時就需要重置sum為0,從a[2]再開始累加,若a[2]也小於零,再重置為0,從a[3]開始累加,以此類推。

2.累加過程中sum>0 例如a[1]為正的(此時sum=a[1]),那麼a[1]就很有可能是最優子串行的左邊部分,將sum與max比較,維護max的值。若a[2]也是正的(sum=a[1]+a[2])也需要維護max,及時更新max的值。

在sum>0的情況中又會有兩種情況出現:

要加的下乙個值為負值

a.加上這個負數後sum仍然為正值,此時sum不會被置零,max值不會更新。(此時sum較之前變小了,max儲存的是前面計算的子串行的和)

b.加上這個負數後sum為負值,此時就要置零sum,重新來計算下乙個可能的最優子串行,此時max值儲存的是左邊的可能最優子串行的值。

以此進行下去,直到遍歷完整個序列。

sum的值維護了子串行的左邊界,max的值維護了子學列的右邊界,都來保證子串行的和最大。

上述分析可寫成下面的偽**:

sum = a[1] //初始化

max = -∞

for i <- 2 to n //2到n遍歷,初始化時已經算上a[1]了

if sum < 0 //sum小於零

sum = a[i] //重置sum

else //sum大於零

sum += a[i] //累加sum

if sum > max //維護max值

max = sum

簡化的c**:

sum = 0;

max = -∞;

for(i=1;i<=n;i++)

細節的地方:

有些地方往往沒有說序列有正有負,可能全是負數,此時就會出現乙個麻煩:上面的**執行完後,最大為0.

有可能序列的第乙個值就是負值,這時執行**時,sum不是被處理成了0就是被處理成了第二值,導致max值在i=1的時候不準確,這一點在需要打表的地方尤為重要,然而poj 2479正是在這裡為難大家。

那麼應該怎麼改呢?

既然只是第乙個值的問題,那麼就可以對第乙個值進行特殊處理,令max=a[1],就能解決這個問題

於是改過的**就是:

sum = 0;

max = a[1];

for(i=1;i<=n;i++)

來看一看上下兩段**處理這兩個序列的不同結果:

1.-3 4 6 1 -3

未注意細節的

注意了細節的

summax

summax

-3->0

-∞-3->0-34

0->4

40->4

104->10

104->10

1110->11

1110->11911

911對結果無太大影響,最終都是11

2.-2 -3 -5 -1 -4

未注意細節的

注意了細節的

summax

summax

-2->0

0-2->0

-2-3->0

0-3->0

-2-5->0

0-5->0

-2-1->0

0-1->0

-1-4->0

0-4->0-1

如果碰上了全負的序列,未注意細節的演算法就是一場災難。

poj 2479中就需要注意這樣的細節,雙向的最大子序和問題關鍵在於分割,可將序列從a[k]處分開成a[1..k],a[k+1..n](1

在實際的過程中,先會遍歷k從1到n-1,求出a[1.k]的最大子序和,打乙個表比方說叫left[k],再遍歷k從n到2,求出a[k..n]的最大子序和,再打乙個表比方叫right[k],再遍歷k從1到n-1,求出最大的left[k]+right[k+1]

轉移方程為:

res = max

ac**:

#include int data[50005];//資料 

int left[50005];//left[i]表示從1到i的最大子序和值

int right[50005];//right[i]表示從i到n的最大子序和值

int main()

left[j] = max;

} //--------------------------------

//-----------計算右邊--------------

max = data[n];

sum = data[n];

right[n] = data[n];

for(j=n-1;j>=1;j--)

right[j] = max;

} //-------------------------------

max = -(1<<30);

for(j=1;j<=n-1;j++)

printf("%d\n",max); }

return 0;

}

POJ 2479的簡單想法

這道題是簡單的dp例題拓展,問題是 給定一行數字,然後求這行數字中兩個連續子串行和的最大值,並輸出 兩個子串行不能有重疊的部分 由於是初學者,可能想法比較侷限。我的總體思路是這樣 這題肯定和最大求子序列有關,然後進行工作 用dp1 i 表示以第i個數結尾的最大子串行的和,用dp i 表示已第i個數為...

poj2479分步DP精簡成O n 演算法

提取資訊包括從s到t之間的和 但是n較大這樣dp i j 的形式顯然不行,那麼如果轉換 其實我們除了記錄 i,j 之間的和那麼可以記錄以i開頭和以i結尾兩種情況 後來發現根本不用記錄以什麼為開頭或者結尾,每次更新前面一段值就行 725k 438ms include using namespace s...

最大子段和

設a 是n個整數的序列,稱為該序列的子串行,其中1 i j n.子串行的元素之和稱為a的子段和.例如,a 2,11,4,13,5,2 那麼它的子段和是 長度為1的子段和 2,11,4,13,5,2 長度為2的子段和 9,7,9,8,7 長度為3的子段和 5,20,4,6 長度為4的子段和 18,15...