Google 面試題 | 最優賬戶結餘

九章演算法發表於2017-05-06

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

題目描述

一群朋友去度假,有時互相借錢。

例如,愛麗絲為比爾的午餐支付了 10 美元。後來克里斯給愛麗絲 5 美元搭計程車。我們可以假設每筆交易為一個三元組(X,Y,Z),這意味著第 X 個人借給第 Y 個人 Z 美元。假設 Alice,Bill 和 Chris 是第0,1,2 個人(0,1,2是他們的ID),他們之間的交易可以表示為[ [ 0,1,10 ],[ 2,0,5 ] ]。

給定一組人之間的交易清單,返回結算所需的最低交易數量

樣例 1

輸入 [[0,1,10], [2,0,5]]

輸出 2

樣例解釋
第 0 個人借給第 1 個人10美元
第 2 個人借給第 0 個人 5美元

需要2筆交易,其中一種方式是第1個人還給第0個人和第2個人各5美元。

樣例 2

輸入 [[0,1,10], [1,0,1], [1,2,5], [2,0,5]]

輸出 1

樣例解釋
第0個人借給第1個人10美元
第1個人借給第0個人1美元
第1個人借給第2個人5美元
第2個人借給第0個人5美元

只需要1筆交易,第1個人還給第0個人4美元債務就還清了。

解題思路

(一)

讀完題我們會發現每個人都有一個借錢的數量和一個還錢的數量(有可能是0),如果這個人的這兩個值的和為0,那麼他就不需要還錢或者借錢給別人了。

證明:假設此人在借還之和為0的情況下收到 X 的錢,那麼為了收支平衡,必定要把這些錢給另外一個人 Y,那麼這樣不會比X把錢直接給 Y 得到的答案更優。

比如,設三個人的收支情況為[-2,0,2],那麼第3個人把2轉移到第2個人,再由第2個人轉移到第1個人,需要2次交易,但是第3個人直接轉移給第1個人那麼只需要1次。

(二)

接下來我們只需要處理所有收支不為0的那些人。我們發現,(在資料合法的情況下)所有人的收支情況的和也是0,那麼我們就來分析一下如何讓答案最小。

對於預處理完收支情況的一個陣列 2 , 3 , -2 , -3 ,顯而易見答案是2 (2 → -2 , 3 → -3 ),但是還有一種不是最優的答案3(3 → -2 , 1 → -3 , 2 → -2)。那麼我們就能發現,最優答案是2個子問題([2,-2],[3,-3])的最優解的和。

我們可以用集合列舉所有的子集,找到每個子集的最優解從而解得總問題的最優解。在這裡我們可以把一個集合的子集定義為一個只有 0,1 的向量,

比如一個有3個元素的集合,他們的子集分別是000(空集),001,010,100,011,110,101,111(全集),'1'代表這個子集裡有這個元素,'0'代表這個子集裡沒有這個元素。

再利用一個1~n迴圈來判斷這個子集裡有哪些元素,如101代表這個子集裡有第1個和第3個元素。

(三)

接下來我們分析這種解法的時間複雜度,假設去除所有收支平衡以後的人數為n,列舉子集的時間複雜度為O(2^n),對每個子集進行最優解分析也需要列舉它的所有子集(這些子集的最優解已經計算完成),需要的時間複雜度也為O(2^n),最終的時間複雜度為O(4^n)。空間複雜度為O(2^n)。

參考程式碼

Google 面試題 | 最優賬戶結餘

面試官角度分析

本題難度較大,可能會有多種解法,比如深度優先搜尋(需要列舉所有可能的交易情況)等,效率較低。

假如運用這種預處理+動態規劃或更高效的方法,那麼有 strong hire 的水平,如果能用其他解法(如搜尋)解出,則有 hire 的水平。

九章答案連結

www.jiuzhang.com/solutions/o…

LintCode 相關題目

k 數和: http://www.lintcode.com/zh-cn/problem/k-sum/
打劫房屋:www.lintcode.com/zh-cn/probl…


推薦閱讀:



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

Google 面試題 | 最優賬戶結餘
九章演算法,IT教育領域的深耕者

相關文章