Facebook 面試題 | Backpack VI 揹包演算法

九章演算法發表於2017-10-25

專欄 | 九章演算法
網址 | www.jiuzhang.com

題目描述

給一個nums[]陣列,如[1, 2, 4]
將這些陣列合使得: 這些數的和是給出的一個target,如使這些數的和等於4
求這樣的組合有多少個?

樣例
[1, 1, 1, 1]
[1, 1, 2]
[1, 2, 1]
[2, 1, 1]
[2, 2]
[4]

這些組合都可以,他們的和都是4
所以答案是6

演算法分析

1.直觀的想法就是把所有的組合全都找出來,再計算他們的個數就可以了。那麼,怎麼找所有的組合呢?首先你有一個nums陣列,一個target。首先想,能不能把這個問題分解成子問題?可以。以題目描述為例,需要[1,2,4]組合成4,可以分解為3個子問題:

序列的第一個數為1,讓[1,2,4]組合成3
序列的第一個數為2,讓[1,2,4]組合成2
序列的第一個數為4,讓[1,2,4]組合成0

2.上述的想法用DFS就可以實現,設N為nums陣列的長度,M為target,但是複雜度達到了O(N^M),這個複雜度太高了。 換一個角度來想一想,假設dp[i]為讓[1,2,4]為i的組合的數目,如果我dp[1],dp[2],dp[3]都已經求出來了,如何去求dp[4]呢?dp[4]可以由哪些子問題得到呢?這個思路和上面的過程是一樣的。

序列的第一個數為1,加dp[3]得到
序列的第一個數為2,加dp[2]得到
序列的第一個數為4,加dp[0]得到

3.dp[0] = 1

dp[1] = 第一個數為1,然後加dp[0]得到 = dp[0] = 1
dp[2] = 第一個數為1,然後加dp[1] + 第一個數為2,然後加dp[0] = dp[1] + dp[0] = 2
dp[3] = 第一個數為1,然後加dp[2] + 第一個數為2,然後加dp[1] = dp[2] + dp[1] = 3
dp[4] = 第一個數為1,然後加dp[3] + 第一個數為2,然後加dp[2] + 第一個數為3,然後加dp[1] = dp[3] + dp[2] + dp[1] = 6

4.於是dp方程為dp[i] = dp[i-nums[1]] + dp[i-nums[2]] + ... + dp[i-nums[N]],初始化dp[0] = 1。複雜度O(NM)。

可以發現,第一種思路和第二種思路其實很相近,他們的區別就是,第二種思路對所有的子過程只計算了一遍,第一種思路可能對一個子過程計算很多遍,例:設dfs(i)可以求出目標為i的結果,dfs(4) = dfs(3) + dfs(2) + dfs(0) = dfs(2) + dfs(1) + dfs(2) + dfs(0) 展開到這一步的時候就可以發現dfs(2)已經被計算了2遍了,而在第二種思路中,每個子過程只會被計算一遍,那麼,可不可以讓第一種想法計運算元過程的時候只計算一遍呢?可以,那就是記憶化搜尋。

5.因為題目沒說資料是否是遞增,可以考慮給陣列排序,排序過後 一旦遇到 i - nums[j] < 0的,就可以直接退出迴圈。

參考程式碼

面試官角度分析

這道題目的是揹包問題當中比較難的面試題目,如果會做01揹包,能夠擴充想到這道題目多重揹包怎麼樣處理n個揹包數目,那麼這道題目就能夠迎刃而解,能夠想出O(n*target)時間複雜度的演算法那麼久可以達到hire

LintCode相關聯絡題

登陸www.lintcode.com, 搜尋 backpack即可。

Backpack-i
Backpack-ii
Backpack-iii
Backpack-v


推薦閱讀



歡迎關注我的微信公眾號:九章演算法(ninechapter)。
精英程式設計師交流社群,定期釋出面試題、面試技巧、求職資訊等

九章演算法,IT教育領域的深耕者
九章演算法,IT教育領域的深耕者

相關文章