最大連續子串行求和的多種解法

2021-10-03 09:32:24 字數 3211 閱讀 1566

題目鏈結

【題目描述】洛谷1115最大連續子串行求和

給出一段序列,選出其中連續且非空的一段使得這段和最大。

【輸入格式】

第1行是乙個正整數n(n≤200000),表示了序列的長度。

第2行包含n個絕對值不大於10000的整數a[i],描述了這段序列。

【輸出格式】

輸出僅包括1個整數,為最大的子段和是多少。子段的最小長度為1。

暴力列舉o(n3)

列舉——優化o(n2)

列舉——再優化o(n)

分治o(nlog2n)

動態規劃o(n)

對於此問題的求解與優化,讓人不得不折服於演算法的魅力我就是這麼入坑的。

大多數題目不出意外第一想法基本都是列舉;對於這個題目的暴力其實不難想到,題目求解的是乙個連續區間的和,區間必然有左右端點,因此直接列舉左端點l和右端點r,知道區間了求和還不是分分鐘的事(迴圈累加唄),**大概長得如下:

for

(int l=

1;l<=n;l++

)//列舉左端點

for(

int r=l;r<=n;r++

)

其實很多演算法不需要實現,通過時空複雜度分析即可;不難看出該演算法時間複雜度高達o(n3),,當n規模超過1000就會tle。不過該演算法還可以通過「字首和」優化。

字首和:與陣列arr宣告乙個一樣大的陣列sum,將arr的前i項和儲存到sum[i],不難得到sum[i]=sum[i-1]+arr[i]

例如:123

4567

8arr13

-342-2

7-3sum141

57512

9應用字首和求解arr[ l到r ]的和:

sum[r]-sum[l-1] = arr[l]+arr[l+1]+……+arr[r]

因此,可以先預處理出字首和,然後用sum[r]-sum[l-1]代替for l to r求和;可優化到o(n2)。分析後發現該時間複雜度依然不能滿足題目要求,當然字首和思想還是不錯的,**大概如下:

sum[0]

=0;for

(int i=

1;i<=n;i++

)sum[i]

=sum[i-1]

+arr[i]

;for

(int l=

1;l<=n;l++

)for

(int r=l;r<=n;r++

) ans=

max(ans,sum[r]

-sum[l-1]

);

列舉演算法至此,似乎已經山窮水盡呢,因為區間必然有左右端點,似乎最少也得兩個迴圈,也就是說最少也得o(n2)。

事實上,並非如此,在字首和的基礎上,可以再優化。

再上述演算法中不難看出核心**就一句:

ans=

max(ans,sum[r]

-sum[l-1]

)

如果我們固定r,那麼這句話可以理解為,找到乙個最好的左端點,使得最終的和最大。

顯然,r固定後,sum[r]是不會改變的,因此要使sum[r]-sum[l-1]盡量大,只要找到最小的sum[l-1]即可。

因此,列舉右端點r,並維護乙個前r項的最小和mins即可。可省去列舉左端點的迴圈,從而將演算法優化到o(n)。

#include

using

namespace std;

const

int n=

2e5+5;

int sum[n]

,ans=

-n,n,mins=0;

intmain()

printf

("%d"

,ans)

;return0;

}

分治思想:大事化小,小事化了。

假設要求解的區間為[l,r],l,r的重點為mid,而且最大和的那一段為[x,y],則[x,y]與mid只有三種關係:

[x,y]完全處於mid左邊,即x≤y[x,y]橫跨mid,即x[x,y]完全位於mid右邊,midx

對於第1種和第3種情況,顯然是乙個子問題。大問題是求解[l,r]中的[x,y],情況1和情況3是求解[l,mid]和[mid+1,r]中的[x,y];因此屬於規模更小的子問題,直接遞迴求解即可。

再看情況2,橫跨mid,則表明最優解一定是mid左邊一部分,右邊一部分,因此只需要求出以mid結尾的最優左子段lsum,以及mid+1的最優右欄位rsum,lsum+rsum就是第2種情況的最優解。

最後,返回三種情況中的最大值即可。

#include

using

namespace std;

const

int n=

2e5+6;

int arr[n]

,n;int

getmax

(int l,

int r)

intmain()

動態規劃求解此題是乙個經典演算法,決策也很簡單,因為對於數字arr[i],它只有兩種選擇:

狀態方程也比較容易,f(i)表示以第i個數結尾的最大連續子串行的 和

對於數字i的決策:

狀態轉移方程:f(i)=max(f[i-1],0)+arr[i]

只需要掃瞄一次arr[i],因此時間複雜度為o(n),當然仔細分析會發現陣列arr其實以不要。因為只用了一次,完全可以直接用f[i]代替arr[i]

#include

const

int n=

2e5+6;

int f[n]

,n,ans=

-2e+9;;

intmain()

printf

("%d"

,ans)

;}

乙個題目從不同的角度出發,可能會有不同的解法。

一種演算法是否可行,不一定要實現,通過分析就能知道大概。

有時候正解可能是在某個暴力演算法的基礎上優化而來(解法3),這個優化可能是某種思想(極大,極小,倍增,差分),也有可能需要採用一切特殊的資料結構(堆或 優先佇列,單調佇列,樹狀陣列,線段樹,st表)。

最大連續子串行求和

time limit 1 s memory limit 32 mb submitted 120 accepted 67 64bit integer format lld submit 給定k個整數的序列,其任意連續子串行可表示為,其中 1 i j k。最大連續子串行是所有連續子串行中元素和最大的乙個...

最大連續子串行求和

問題 有一串 int 數字串,存在陣列a中。要求找到起始位置start和終止位置end,使得從start位置到end位置的所有數字之和最大,返回這個最大值max。演算法思路 設 suffer 為以a x 終止且包含a x 的最大序列的和,有 if suffer a x 1 max suffer a ...

最大連續子串行求和

hz偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會後,他又發話了 在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全為正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,並期望旁邊的正數會彌補它呢?例如 連續子向量的最大和為8 從第0個開始,到第...