快手的一道演算法面試題,鏈結 快手面試題:善變的同伴
其實抽象出來就是 最大m子段和
本文參考的是這位c++版,但其側重講的是優化
給定由n個整數(可能為負)組成的序列a1、a2、a3…,an,定義dp[ i ] [ j ]為 前 j 項所構成的 i 個子段的最大和,且必須包含著第j項,即以第j項結尾以及乙個正整數m,要求確定序列的m個不相交子段(內部是連續的),使這m個子段的總和最大!
例如這個序列是 -2 , 1, 2 , -9, 2 , -1, 3, -4 要求挑出2個子段,那麼應該這麼挑,才能保證2個子段的和最大 : -2 , 1, 2 , -9 , 2 , -1, 3 ,-4 最大值為 3 + 4 = 7
特別注意: 有些題目可能不存在負數答案,給出的序列全是負數,那麼不管m是多少,答案是0。此時選擇的子段是0個,不足m個,例如快手的那個面試題
也可能有些題目要求,不管結果正負,必須選夠m個子段。例如本文所演示的樣例
區別在dp陣列的初始化。前者要求dp初始為0,後者要求第0行為0,其餘為負無窮
那麼求dp[ i ] [ j ]有兩種可能 (取較大的那個):
1、dp[ i ][ j ] = dp[ i ] [ j-1 ] + a[ j ] ,意思是第 j 項融合到第 j-1 項所屬的那個子段中,子段數沒變
2、dp[ i ][ j ] = dp[ i-1 ] [ t ] + a[ j ],意思是第 j 項單獨成乙個子段,其中的t < j ,下面圖中可以看到它的含義。案例:給定原始陣列 -2, 11,-4, 13,-5, 6,-2
求子段數為 1.2.3…7時的結果。
初始化: 開二維陣列,大小為 d [ m+1 ] [n + 1],初始化全0
m 是子段數,n是原始資料的長度
填表:
例如在求 i = 3, j = 6 時,也就是圖中的 30 是怎麼得來的?
看一下 dp 的遞推關係式,
dp[ i ][ j ] = dp[ i ] [ j-1 ] + a[ j ] ,也就是30前面乙個數 19 + 6 = 25 了填完錶後,可以看到,dp[ i ][ j ] = dp[ i-1 ] [ t ] + a[ j ] 其中的 dp [ i-1 ][ t ] 是當前位置前面所有值的最大值,也就是圖中紅框中9,7,24,19的最大值,是24,再加上 6 ,就是30了。因為30 比 25 大,所以是30
m = 1 時,是第 1 行的最大值,為 21m = 2 時, 是第二行的最大值, 為26
…m = 7 時,每個數單獨成一段,和為 17.
private
static
void
fun(
int n,
int m,
int[
] nums)
}}
我們剛才用乙個值來記錄紅框中的最大值並每步更新它,已經算是乙個正常的小優化,因為不然的話,每次都要去遍歷來找紅框中的最大值。
事實上還有兩個優化的方式,如果只是讓我們求 m 為某個值時的答案。
1.因為每次只用到了當前行和上一行的資料,dp 可以降成 1 唯陣列。優化後的 時間複雜度大致為 o(m*(n-m+1))2.每次計算表中當前值時,其上方和右上方的值沒有參與計算,所以沒有用,可以跳過。
例如 m = 3 時,藍色的值是不需要計算的。
空間複雜度為 o(n)
面試題 連續子陣列的最大和
有乙個整型陣列,裡面的元素有正數和負數,乙個或者連續的多個元素組成乙個子陣列,求所有子陣列的和的最大值。也就是連續子陣列最大和問題。這道題一般使用動態規劃來做,面試的時候問到也是考察動態規劃。f i 表示以i結尾的子陣列的最大和。既然是以i結尾,那從何處開始呢?j在i的左邊,如果a 0 到a j 的...
面試題31 連續子陣列的最大和
題目 輸入乙個整形陣列,陣列裡有正數也有負數,陣列中乙個或連續的多個整數組成乙個子陣列。求所有子陣列的和的最大值。要求時間複雜度為o n 例如,輸入陣列 1,2,3,10,4,7,2,5 和最大的子陣列微3,10,4,7,2,因此輸出該子陣列的和18 思路 1,分析陣列規律 初始化和0,第一哥數字1...
面試題27 連續子陣列的最大和
注意 當函式輸入無效時,返回為0,而子陣列的和也有可能為0,為了區分,設定乙個全域性變數標記輸入是否有效。思路用下表說明 bool binvalidinput false 用全域性變數標記是否為無效輸入 求連續子陣列的最大和 int findgreatestsumofsubarray int nar...