動態規划之最大子段和問題


問題描述:

給定長度為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;

 最大子段和變形:記錄開始和結束點


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM