1. 程式人生 > >最大連續子序列和:動態規劃經典題目(2)

最大連續子序列和:動態規劃經典題目(2)

問題描述

      連續子序列最大和,其實就是求一個序列中連續的子序列中元素和最大的那個。

      比如例如給定序列:

           { -2, 11, -4, 13, -5, -2 }

        其最大連續子序列為{ 11, -4, 13 },最大和為20。


===============================================================

問題分析

1.首先最樸素的方法是暴力 O(n^3)

直接兩個for迴圈列舉子序列的首尾,然後再來個迴圈計算序列的和,每次更新和的最大值。

        但是這種方法的複雜度是O(n^3),效率實在太低了。。。

————————————————————————————————————————————————

2.第二種方法是預處理 O(n^2)

  在讀入的時候將前面數的和放在陣列中,就能得到一個數組sum[i]儲存前i個數的和。然後兩重迴圈列舉首尾,利用sum陣列迅速求出子序列的和。

        其實這種方法只是優化了前面那種方法的計算和的迴圈,複雜的是O(n^2),也很糟糕。
————————————————————————————————————————————————

3.第三種是利用

分治思想 O(nlogn)

分治演算法但看程式碼不是很好理解,其實思想很簡單,就是把序列分成兩塊計算,用遞迴分別求出兩塊序列中的最大子序列和,然後從序列中間向兩邊遍歷求出包含中心的序列的最大和。返回最大的那個序列和。

       遞迴真的很神奇,一直把問題分解乘小問題交給下一層遞迴處理,直到最底層。

       用分治演算法的複雜度好了一些,是O(nlogn),雖然不是最優解,但是理解這種演算法的確能讓我們對遞迴理解得更加深刻。
————————————————————————————————————————————————

4.第四種是累積遍歷演算法O(n)

  遍歷序列的時候對Sum進行累計,如果Sum累積後小於0的話就把Sum重置為負無窮,每次更新Sum的最大值。最後便能求出最大值。

       其實這個演算法就是把序列分為好幾塊,每一塊滿足:對於任意k,前k個數的和不會小於0(小於0就會分裂成兩塊了),當前i個數的和大於最大值時就進行更新,而最大值的左邊界就是該塊序列的第一個,右邊界是第i個。

       時間複雜度為O(n),而且可以一邊讀取一邊處理,不需要開闢陣列來存,空間也很省。

------------------------------------------------------------------------------

   舉個例子: 

-10   1  2  3  4  -5   -23   3  7    -21   (num)

-10 | 1  3  6 10  8 | -23 |  3 10 | -21   (Sum)(|號為該處Sum被清零)

   由於10是Sum的最大值,所以,紅色的那塊就是要求的範圍了。

------------------------------------------------------------------------------

 但是為什麼所求序列為什麼是在序列塊中並且是以序列塊第一個數作為開頭呢?

:如果不是這樣的話,會有幾種情況:

          1  該目標序列跨越了幾個塊。由於Sum在<=0的時候會重置為負無窮,如果跨塊的話,沒有重置那它的和肯定不會是最大的。

          2  該目標序列在序列塊中間,且目標序列的開頭並不是序列的第一個。由於序列塊前k個數的和不會小於0,所以這樣肯定沒有從第一個開始來的大。

————————————————————————————————————————————————

5.第五種是動態規劃 O(n)

        dp做法是很普遍的做法,只要想出狀態轉移方程就可以很快做出來了。

        狀態轉移方程:sum[i] = max{sum[i-1]+a[i],a[i]}. (sum[i]記錄以a[i]為子序列末端的最大連續和。)在dp的過程中便可以更新sum陣列的最大值以及兩個邊界。

        其實完全可以不用開陣列,累計sum直到sum + a < a,把sum賦值為a,更新最大值就行了。你會發現這跟第4種方法是一樣的。。。只是判斷條件不一樣,一個是sum <= 0一個是sum + a < a。。。(其實是一樣的。。。)所以複雜度和第四種是一樣的都是O(n)。


————————————————————————————————————————————————

6.第六種是第二種累計求和 O(n)

       (感謝@shuangde800 大神的指導)

         這種方法跟第4種一樣是累計求和。我們可以發現,連續子序列的和其實就是 (到尾端的和)-(到首端-1的和),要讓這個式子最大其實可以讓(到首端-1的和)儘量的小,我們可以遍歷陣列,維護(到首端-1的和)的最小值。

         理解起來就是遍歷的時候,找出以當前位置為尾端,以此前的某一點為首端的 和最大的子序列,然後更新最大值。(其實這個思想和第五種方法是異曲同工的)

         由於只需要一遍遍歷,所以複雜度為O(n)。

===============================================================

虛擬碼:

由於下面的Solution裡面我的程式碼太挫,於是先貼出虛擬碼。

1.暴力 O(n^3):

[plain] view plain copy  print?
  1. max←(-∞)  
  2. for i←1 to len do  
  3.     for j←i to len do  
  4.         sum←0  
  5.         // 求和  
  6.         for k←i to j  
  7.             sum←sum+arr[i]  
  8.         end for  
  9.         // 更新最大值  
  10.         if sum>max then  
  11.             max←sum  
  12.         end if  
  13.     end for  
  14. end for  
  15. return max  

2.預處理 O(n^2): [plain] view plain copy  print?
  1. // 記錄前i項和  
  2. sum[0]←0  
  3. for i←1 to len do  
  4.     sum[i]←sum[i-1]+arr[i]  
  5. end for  
  6. // 列舉首尾  
  7. max←(-∞)  
  8. for i←1 to len do  
  9.     for j←i to len do  
  10.         // 更新最大值  
  11.         if sum[j]-sum[i-1]>max then  
  12.             max←sum[j]-sum[i-1]  
  13.         end if  
  14.     end for  
  15. end for  
  16. return max  

3.分治演算法 O(nlogn) [plain] view plain copy  print?
  1. function maxsum(arr,left,right)  
  2.     // 只有一個元素就返回  
  3.     if right-left=1 then  
  4.         return  
  5.     end if  
  6.     // 劃分[left,mid)和[mid,left)分治  
  7.     mid←x+(y-x)/2  
  8.     L←maxsum(arr,left,mid)  
  9.     R←maxsum(arr,mid,left)  
  10.     if L>R then  
  11.         max←L  
  12.     else  
  13.         max←R  
  14.     end if  
  15.     // 求出處於中間的連續子序列最大和  
  16.     // 先求從中間向左最大值  
  17.     tempSum←0  
  18.     leftSum←arr[m-1]  
  19.     for i←m-1 downto right do  
  20.         tempSum←tempSum+arr[i]  
  21.         if leftSum<tempSum then  
  22.             leftSum←tempSum  
  23.         end if  
  24.     end for  
  25.     // 再求從中間向右最大值  
  26.     tempSum←0  
  27.     rightSum←arr[m]  
  28.     for i←m to left do  
  29.         tempSum←tempSum+arr[i]  
  30.         if rightSum<tempSum then  
  31.             rightSum←tempSum  
  32.         end if  
  33.     end for  
  34.     midSum←leftSum+rightSum  
  35.     if max<midSum then  
  36.         max←midSum  
  37.     end if  
  38.     return max  
  39. end function  

4.累積遍歷演算法 O(n)
[plain] view plain copy  print?
  1. sum←0  
  2. max←arr[0]  
  3. for i←1 to len do  
  4.     if sum<=0 then  
  5.         sum←a[i]  
  6.     end if  
  7.     if max<sum then  
  8.         max←sum  
  9.     end if  
  10. end for  
  11. return max  

5.動態規劃 O(n)
[plain] view plain copy  print?
  1. sum←(-∞)  
  2. max←(-∞)  
  3. for i←1 to len do  
  4. 相關推薦

    連續序列動態規劃經典題目(2)

    問題描述:       連續子序列最大和,其實就是求一個序列中連續的子序列中元素和最大的那個。       比如例如給定序列:            { -2, 11, -4, 13, -5, -2 }         其最大連續子序列為{

    連續序列遞迴動態規劃

    問題描述: 給定一個整數序列,a0, a1, a2, …… , an(項可以為負數),求其中最大的子序列和。如果所有整數都是負數,那麼最大子序列和為0; 方法一: 用了三層迴圈,因為要找到這個子序列,肯定是需要起點和終點的,所以第一層迴圈確定起點,第二層迴圈確定終點,第三層

    連續序列之和(動態規劃

    1. 問題描述 設n個元素的序列儲存在陣列A[0..n-1]中,求陣列中連續子序列之和的最大值。 2. 遞推公式 設All[i]為子問題A[i..n-1]的連續子序列之和的最大值,start[i]為從A[i]開始的連續序列之和的最大值,因此: All[i] = A[n-

    動態規劃dp經典題目連續序列

    最大連續子序列和問題         給定k個整數的序列{N1,N2,...,Nk },其任意連續子序列可表示為{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= k。最大連續子序列是所有連續子序中元素和最大的一個,例如給

    動態規劃——連續序列

    最大連續子序列和問題如下:       下面介紹動態規劃的做法,複雜度為 O(n)。   步驟 1:令狀態 dp[i] 表示以 A[i] 作為末尾的連續序列的最大和(這裡是說 A[i] 必須作為連續序列的末尾)。   步驟

    hdoj1003+codeup2086Max Sum連續序列問題解法總結

    目錄 hdoj 1003求解方法 暴力求解O(n^3)/O(n^2)(不推薦,很可能會超時) 分治法(比較複雜,掌握思想即可) 遍歷求和法O(n) dp動態規劃(強推) codeup2086的求解方法 dp求解 hdoj 1003求解方法 暴力求解

    動態規劃(三)長遞增序列LIS、連續序列連續序列乘積

    最長遞增子序列LIS 問題 給定一個長度為N的陣列,找出一個最長的單調自增子序列(不一定連續,但是順序不能亂)。例如:給定一個長度為6的陣列A{5, 6, 7, 1, 2, 8},則其最長的單調遞增子序列為{5,6,7,8},長度為4. 最長遞增子序列

    動態規劃專題—連續序列—A1007

    最大連續子序列和問題如下 給定一個數字序列A1,A2,…,An,求i,j(1<=i<=j<=n),使得Ai+….+Aj最大,輸出這個最大和。 樣例:-2 11 -4 13 -5 2 1、令狀態dp[i]表示以A[i]為結尾的連

    動態規劃的基礎篇1--連續序列

    上篇已經稍微介紹了什麼是dp,接下來就是實戰了。今天七夕節,祝願大家有人陪伴,好好珍惜。。哎。。 給定一個數字序列A1,A2,A3,A4,A5,A6,A7,A8,A9.....An。求i,j(1<=i<=j<=n)使得Ai++++++Ai最大,輸出這個最大

    (4)C語言——求連續序列

    log spa clas 最大連續子序列和 alloc 最大 code max 連續 題目: 輸入一組整數,求出這組數字子序列和中最大值。也就是只要求出最大子序列的和,不必求出最大的那個序列。例如: 序列:-2 11 -4 13 -5 -2,則最大子序列和為20。 序列:-

    連續序列

    運行時 介紹 最大連續子序列和 () vector 運行 n) else 連續子序列和 下面介紹一個線性的算法,這個算法是許多聰明算法的典型:運行時間是明顯的,但是正確性則很不明顯(不容易理解)。 //線性的算法O(N) long maxSubSum4(const vec

    連續序列/長不下降序列/長公共序列/長迴文

    //最大連續子序列和 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 10010; int A[maxn],dp[maxn]; int main(){ int

    連續序列可能的優解

    問題描述: 給定一個整數序列,a0, a1, a2, …… , an(項可以為負數),求其中最大的子序列和。如果所有整數都是負數,那麼最大子序列和為0; 解決這個問題的演算法有很多種,比如兩重迴圈的暴力破解,或者利用分治的思想,但是還有一種線性時間複雜度的演算法:線上處理,可以比較好的解決這

    dp經典問題-連續序列 hdu1003

    題目描述: 這道題我先後做過三遍,結果每一遍都沒有做出來。今天再仔仔細細的研究了一下,才發現用動態規劃更好理解。 關於求最大連續子序列和的博文轉載如下:https://www.cnblogs.com/coderJiebao/p/Algorithmofnotes27.html 最大連續子序列和的特點就

    dp經典問題-連續序列 hdu1003

    i++ 最長子序列和 cnblogs ++ n) 代碼 str .com 個數 題目描述: 這道題我先後做過三遍,結果每一遍都沒有做出來。今天再仔仔細細的研究了一下,才發現用動態規劃更好理解。 關於求最大連續子序列和的博文轉載如下:https://www.cnblogs.

    Python語言描述連續序列

    1.問題描述 假設有一陣列(python裡為list啦)[1,3,-3,4,-6,-1],求陣列中最大連續子序列的和。例如在此陣列中,最大連續子序列的和為5,即1+3+(-3)+4 = 5 2.O(n2)的解法 最簡單粗暴的方式,雙層迴圈,用一個maxsum標識最大連續子序列和。然後

    PAT甲級 連續序列

    目前PAT甲級中涉及Dijkstra的題目有如下幾題: 最大連續子序列和提供兩種寫法: 1.動態規劃: int a[maxn],dp[maxn];//a[i]存放序列,dp[i]存放以a[i]結尾的連續序列的最大和 dp[0]=a[0]; for(int i=1;i&l

    [POJ1050]To the Max (矩陣,連續序列)

    資料弱,暴力過 題意 N^N的矩陣,求最大子矩陣和 思路 懸線?不需要。暴力+字首和過 程式碼 //poj1050 //n^4暴力 #include<algorithm> #include<cstdio> #include<cstring>

    hdoj Max Sum 連續序列問題

     這個題目是最大連續子序列和的擴充套件,在此基礎上加了起始點和終止點。 (1)終止點的確定,就是每當更新一次Max的值時,更新一下終止點。 (2)起始點的確定,當Thissum<0時,給一個暫時起始點的標記,然後當Max的值更新時,將起始點的值一起更新。 程式

    1007 Maximum Subsequence Sum(25 分)【連續序列

    題意:求最大連續子序列和並記錄該序列的頭尾元素 #include <bits/stdc++.h> using namespace std; int N; int main() { cin>>N; vector<int>