1. 程式人生 > >從暴力求解到動態規劃—— 7 種方法求解連續子陣列的最大和問題(python實現)

從暴力求解到動態規劃—— 7 種方法求解連續子陣列的最大和問題(python實現)

問題描述

已知一個數組 a[n],裡面存放著浮點數,可能是正數、負數或0。求它的所有連續子陣列中的最大和。

連續子陣列:指的是陣列的一個連續切片,即可以表示為 a[i:j],0≤i≤j<n

連續子陣列的和:比如連續子陣列為 a[i:j] ,則和為 a[i] + a[i+1] + ... + a[j]

比如 a = [31,-41,59,26,-53,58,97,-93,-23,84],則問題的最優解為 187,對應的子陣列為 [59,26,-53,58,97]

求解思路

1、暴力求解。枚舉出所有連續子陣列,計算每個子陣列的和,求最大值。時間複雜度為 O(n^3) 或 O(n^2),具體見下面的前3個函式。

2、分治遞迴。每次將陣列對半分,分別求取兩部分的最優解,以及包含兩部分的銜接處的最優解。時間複雜度為 O(nlogn)。

3、動態規劃。時間複雜度為 O(n)。有3種方法來實現。每種方法的解釋見下面函式的註釋。

# -*- coding: utf-8 -*-
"""
Created on Tue Jul 31 22:01:19 2018

@author: gongyanshang1

輸入一個包含n個浮點數的陣列,要求輸出此陣列的任何連續子陣列中的最大和。
特殊地,如果陣列中都是負數,則最大和為0,連續子陣列長度為0。
"""

def find_max_sum_by_force(a):
    '''
    用暴力方法求解,最樸素的邏輯,三重迴圈,複雜度為O(n^3)。
    '''
max_sum = 0 for i in range(0, len(a)): for j in range(i, len(a)): sum = 0 for idx in range(i, j+1): sum += a[idx] if sum > max_sum: max_sum = sum return max_sum def find_max_sum_by_force2(a): ''' 暴力求解,將三重迴圈的最內層迴圈去掉,複雜度為O(n^2)。 '''
max_sum = 0 for i in range(0, len(a)): sum = 0 for j in range(i, len(a)): sum += a[j] if sum > max_sum: max_sum = sum return max_sum def find_max_sum_by_force3(a): ''' 暴力求解,提前計算一個累積求和的陣列。複雜度為O(n^2)。 ''' # 先求出從第一個索引出發,到每個索引結束的子陣列的和 sum_list = [0] * (len(a) + 1) # 比a長一位是為了保證sum_list[-1]=0 for i in range(0, len(a)): sum_list[i] = sum_list[i-1] + a[i] # 雙重迴圈 max_sum = 0 for i in range(0, len(a)): for j in range(i, len(a)): sum = sum_list[j] - sum_list[i-1] if sum > max_sum: max_sum = sum return max_sum def find_max_sum_by_recursion(a): ''' 遞迴求解。分治思想。複雜度為O(nlogn) ''' # 定義遞迴結束標誌 if len(a) == 0: return 0 if len(a) == 1: return max(a[0], 0) # 將陣列分為兩個陣列,則原問題的最大和=max(前半部分的最大和,後半部分的最大和,包含中間數的子陣列的最大和) midx = int(len(a) / 2) a1 = a[:midx] a2 = a[midx:] # 計算包含中間數的子陣列的最大和 # 計算左半部分的最大和 lmax = 0 sum = 0 for i in range(midx-1, -1, -1): sum += a[i] if sum > lmax: lmax = sum # 計算右半部分的最大和 rmax = 0 sum = 0 for i in range(midx, len(a)): sum += a[i] if sum > rmax: rmax = sum mid_max = lmax + rmax # 含中間數的子陣列的最大和 = 左半部分的最大和 + 右半部分的最大和 return max(find_max_sum_by_recursion(a1), find_max_sum_by_recursion(a2), mid_max) def find_max_sum_by_dynamic(a): ''' 動態規劃。複雜度為O(n)。思想是已知a[0:i]的最優解,如何求取a[0:i+1]的最優解。 動態規劃本質是將一個個子任務的結果存起來,供下一個子任務使用。空間換取時間。 有點像數學歸納法,已經證明了前i-1步是正確的,然後根據第i-1步和第i步的關聯關係,證明第i步也是正確的。 ''' max_sum = 0 # 儲存當前子陣列的最優解,供下一個子陣列使用 max_ending = 0 # 儲存當前子陣列的以最後一個元素結尾的子陣列的最大和,供下一個子陣列使用 for i in range(0, len(a)): # 每個迴圈求解的是 a[0:i] 的最優解 max_ending = max(0, max_ending + a[i]) # 末尾子陣列的最大和,只有可能是三個值:0、末尾元素、上一個子陣列的末尾子陣列的最大和+末尾元素 max_sum = max(max_sum, max_ending) # 當前子陣列的最優解,只有可能是兩個值:上一個子陣列的最優解、當前末尾子陣列的最大和 return max_sum def find_max_sum_by_scan(a): ''' 掃描方法,複雜度為O(n)。 這個方法是從網上看到的,其實程式碼與動態規劃是完全等價的。 我把它單獨又寫了一個函式,是因為它使用了另一種思路來解釋。 演算法思想: 1、維護一個sum值,從第一個元素開始,依次往後累加 2、比如已經累加了前i個元素,即 a[0]~a[i-1]。如果結果大於0則繼續; 3、如果小於0,則問題最優解只可能是兩個值:a[i:]的最優解、a[:i-1]的最優解。a[i:]的最優解已經儲存起來,因此將sum置0,重新開始累積,來計算 a[:i-1]的最優解。 (這與find_max_sum_by_recursion方法來比,少了一個“包含中間數的子陣列的最大和”。為什麼不必考慮這個值?因為左半部分的最大和一定是0。很容易用反證法來證明此結論。) ''' max_sum = 0 sum = 0 for i in range(0, len(a)): sum += a[i] if sum < 0: sum = 0 max_sum = max(max_sum, sum) return max_sum def find_max_sum_by_scan2(a): ''' 遞迴方法,複雜度為O(n)。 此方法受 find_max_sum_by_scan 方法啟發。既然可以將最優解分解為 a[i:]的最優解、a[:i-1]的最優解 兩部分,那麼為什麼不可以用遞迴呢? ''' # 遞迴停止規則 if len(a) == 0: return 0 max_sum = 0 sum = 0 for i in range(0, len(a)): sum += a[i] if sum < 0: return max(max_sum, find_max_sum_by_scan2(a[i+1:])) # return max(a[i:]的最優解, a[:i-1]的最優解) max_sum = max(max_sum, sum) return max_sum if '__main__' == __name__: a = [31,-41,59,26,-53,58,97,-93,-23,84] print(find_max_sum_by_force(a)) print(find_max_sum_by_force2(a)) print(find_max_sum_by_force3(a)) print(find_max_sum_by_recursion(a)) print(find_max_sum_by_dynamic(a)) print(find_max_sum_by_scan(a)) print(find_max_sum_by_scan2(a))

相關推薦

暴力求解動態規劃—— 7 方法求解連續陣列問題python實現

問題描述 已知一個數組 a[n],裡面存放著浮點數,可能是正數、負數或0。求它的所有連續子陣列中的最大和。 連續子陣列:指的是陣列的一個連續切片,即可以表示為 a[i:j],0≤i≤j<n。 連續子陣列的和:比如連續子陣列為 a[i:j] ,則和為

連續陣列O(n解法:雙指標 動態規劃

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

動態規劃--求目標值問題、找零錢問題以及求連續陣列 --java

1、動態規劃一般可分為線性動規,區域動規,樹形動規,揹包動規四類。 舉例: 線性動規:攔截導彈,合唱隊形,挖地雷,建學校,劍客決鬥等; 區域動規:石子合併, 加分二叉樹,統計單詞個數,炮兵佈陣等; 樹形動規:貪吃的九頭龍,二分查詢樹,聚會的歡樂,數字三角形等;

動態規劃演算法連續陣列,O(N)時間複雜度O(1)空間複雜度 【更新於:2018-05-13】

這個題目最早在13年阿里筆試出現,直到前兩天面試另一家電商又出現,哎,欠的都是要還的。 這個問題的思路如下:一維陣列的下標連續的元素構成連續子陣列,求所有連續子陣列中和最大的那個子陣列。 解析:2018-11-08 1 首先這個問題要轉化為Q(n)的問題,對於Q(n)的

姿勢拿下連續序列問題,附虛擬碼以HDU 1003 1231為例

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

演算法--三方法連續陣列

這是一道考的爛的不能再爛的題目,但是依然有很多公司樂於將這樣的題目作為筆試或面試題,足見其經典。 題目描述: 輸入一個整形陣列,數組裡有正數也有負數。 陣列中連續的一個或多個整陣列成一個子陣列,每個子陣列都有一個和。 求所有子陣列的和的最大值。要求

Maximum Subarray連續序列 -- LeetCode經典動態規劃

原題連結: http://oj.leetcode.com/problems/maximum-subarray/這是一道非常經典的動態規劃的題目,用到的思路我們在別的動態規劃題目中也很常用,以後我們稱為”區域性最優和全域性最優解法“。基本思路是這樣的,在每一步,我們維護兩個變數,一個是全域性最優,就是到當前元

【演算法拾遺】三方法連續陣列

    這是一道考的爛的不能再爛的題目,但是依然有很多公司樂於將這樣的題目作為筆試或面試題,足見其經典。    問題是這樣的:一個整數陣列中的元素有正有負,在該陣列中找出一個連續子陣列,要求該連續子陣列

動態規劃——連續序列

題目描述: 輸入一個整型陣列,數組裡有正數也有負數。 陣列中連續的一個或多個整陣列成一個子陣列,每個子陣列都有一個和。 求所有子陣列的和的最大值。要求時間複雜度為O(n)。 例如輸入的陣列為1,

【演算法之陣列】求陣列的解決方法詳解

題目: 輸入一個整形陣列,數組裡有正數也有負數。 陣列中連續的一個或多個整陣列成一個子陣列,每個子陣列都有一個和。 求所有子陣列的和的最大值。 例如輸入的陣列為1, -2, 3, 10, -4, 7, 2, -5,和最大的子陣列為3, 10, -4, 7, 2, 因此

連續序列問題的四經典解答

問題描述    給定(可能是負的)整數序列A1, A2,...,AN, 尋找(並標識)使Sum(Ak)(k >=i, k <= j)的值最大的序列。如果所有的整數都是負的,那麼連續子序列的最大和是那個最大的負數項。最好給出給出最大和連續子序列!! 1 暴力破

動態規劃求解較小規模的團問題Python實現

1.圖:由點、邊(點與點之間連線),組成的集合,如點集V=[0,1,2,3,4],邊集E=[[1,3,4],[2,3,4],[4],[4],[]],則(V,E)就是一個圖,其表達的意思如下: 該圖中含有5個端點,分別為0,1,2,3,4,這些點存在V中,如端點1對應V

m段的動態規劃

Now I think you have got an AC in Ignatius.L's "Max Sum" problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now y

Leetcode初級演算法 打家劫舍動態規劃Python實現

問題描述: 演算法思想: 該問題的內在邏輯結構依然是動態規劃裡的經典結構。最關鍵的是推出狀態轉移方程,當前規模的對應解法由更低規模的解法,彷彿拾級而上,站在前人的肩膀上不斷攀登。 實際操作起來,比較實用的方法如下:固定一個比較小的規模n, 進行思維實驗。 例子:

利用動態規劃來求出連續項的

#include <iostream> using namespace std; int max(int a,int b) { return a>b?a:b; } int main(int argc, const char * argv[]) { int n

自然語言處理NLP- HMM+VITERBI演算法實現詞性標註解碼問題動態規劃Python實現

NLP- HMM+維特比演算法進行詞性標註(Python實現)     維特比演算法針對HMM解碼問題,即解碼或者預測問題(下面的第二個問題),尋找最可能的隱藏狀態序列:對於一個特殊的隱馬爾可夫模型(HMM)及一個相應的觀察序列,找到生成此序列最可能的隱藏狀態序列。也就是說

【LeetCode & 劍指offer刷題】動態規劃與貪婪法題4:42 連續陣列53. Maximum Subarray

【LeetCode & 劍指offer 刷題筆記】目錄(持續更新中...) 53. Maximum Subarray Given an integer array   nums , find the

【LeetCode & 劍指offer刷題】動態規劃與貪婪法題4:42 連續數組的53. Maximum Subarray

offer pla style other res another () nor pro 【LeetCode & 劍指offer 刷題筆記】目錄(持續更新中...) 53. Maximum Subarray Given an integer array nu

連續陣列基於動態規劃

題目   輸入一個整型陣列,數組裡有正數也有負數。陣列中一個或連續的多個整陣列成一個子陣列。求所有子陣列的和的最大值。要求時間複雜度為O(n)。例如輸入的陣列為{1,-2,3,10,-4,7,2,-5},和最大的子陣列為{3,10,-4,7,2},因此輸出為該子陣列的和18。 思路 一般解法 從

連續數組的基於動態規劃

動態 pty ostream style http 還要 clu ons 連續子數組 題目   輸入一個整型數組,數組裏有正數也有負數。數組中一個或連續的多個整數組成一個子數組。求所有子數組的和的最大值。要求時間復雜度為O(n)。例如輸入的數組為{1,-2,3,10,-4,