問題描述:
給定長度為n的整數序列,a[1...n], 求[1,n]某個子區間[i , j]使得a[i]+…+a[j]和最大.或者求出最大的這個和.例如(-2,11,-4,13,-5,2)的最大子段和為20,所求子區間為[2,4].
1.窮舉法
枚舉左右區間然后遍歷該區間求解,時間復雜度O(n3)
2.窮舉法+前綴和
在第一種方法的基礎上,預處理出前綴和,在枚舉左右區間之后,可以通過前綴和直接求解,例如求[l, r]區間的和,直接用sum[r] - sum[l - 1]求出。時間復雜度O(n2)(前綴和sum[i]表示前i位之和)
3.分治法
求解時分治,[1, n]的最大子段和只可能出現在[1, n / 2]或者[n / 2 = 1, n]或者起點位於[1, n / 2],后者位於[n / 2 + 1, n]。就可以直接分治最大子段和。時間復雜度O(nlog(n))。
1 int maxsum(int *a, int x, int y)//返回左閉右開區間的最大連續和 2 { 3 if(y - x == 1)return a[x];//只有一個元素,直接返回 4 int m = (x + y) / 2; 5 int maxs = max(maxsum(a, x, m), maxsum(a, m, y));//遞歸求解左右區間的最大值 6 int v = 0, L = a[m - 1], R = a[m]; 7 //L為從分界點往左的最大連續和, R為分界點往右的最大連續和 8 for(int i = m - 1; i >= x; i--)L = max(L, v += a[i]); 9 v = 0;//清空之前的v 10 for(int i = m; i < y; i++)R = max(R, v += a[i]); 11 return max(maxs, L + R);//合並求解 12 }
4.動態規划法
設dp[i]為以i結尾的最大子段和,那對於dp[i]而言只有兩種情況,如果dp[i - 1] > 0, 那么dp[i] = dp[i - 1] + a[i];不然,dp[i] = a[i],然后求出dp數組中的最大值即可。
1 ll dp[maxn], a[maxn]; 2 int main() 3 { 4 cin >> n; 5 for(int i = 1; i <= n; i++) 6 { 7 cin >> a[i]; 8 dp[i] = a[i]; 9 } 10 for(int i = 1; i <= n; i++) 11 { 12 dp[i] = max(dp[i], dp[i - 1] + a[i]); 13 } 14 ll ans = 0; 15 for(int i = 1; i <= n; i++)ans = max(ans, dp[i]); 16 cout<<ans<<endl; 17 return 0; 18 }
其實,可以進行空間上的優化,根本不需要dp數組,也不需要a數組,只需要兩個變量即可,一個保存我當前的和,一個保存最大和,如果當前和>最大和,更新最大和,如果當前和<0,則設置當前和 = 0;
1 cin >> n; 2 ll maxsum = 0; 3 ll thissum = 0; 4 int x; 5 for(int i = 1; i <= n; i++) 6 { 7 cin >> x; 8 thissum += x; 9 if(thissum > maxsum) 10 { 11 maxsum = thissum; 12 } 13 if(thissum < 0) 14 { 15 thissum = 0; 16 } 17 } 18 cout<<maxsum<<endl;