記錄一個二進制求集合子集和01揹包的變式.(阿里面試遇到了讓我把01揹包給忘了,不可忍)

抽象出來大概是這樣一個題:

給出一個數組,要求分成兩份,要求兩份和的差值最小.

question one:請用窮舉法來解

面試官說想讓我窮舉每一種情況,看一下我程序設計的思維如何.

假如一共有32個數字,那我用一個32位二進制來枚舉.如果第i位上的二進制爲0,表示這個數字放到第0號集合,爲1表示放到1號集合.

則:將這個32位整數從0加到0xFFFF FFFF就能找出所有情況.

當然,還可以用搜索,,比如DFS來做,不過有點大材小用了.上面說的二進制模擬應該是最優解了.

這也可以叫數位dp吧.

問:如果是分成三份呢?

答:那就用三進制.

question two:那你用最好的方法來做

考慮動態規劃.

假設所有數字加起來爲sum, 

兩個數列要求和最相近,那就轉化爲小的那個數列非常接近sum/2,,也就是不超過sum/2的條件下儘量大.

那我們來複習一下01揹包是個什麼問題:

有n個石頭,每個石頭都有自己的重量w和價值v,現在你有一個能裝m重量的袋子,請問怎麼選取讓價值v儘可能的大.

現在的問題是:

有n個數字,每個數字都有自己的大小,                現在你有一個能裝sum/2數值的數列,請問怎麼選取讓重量儘可能的大.

對比一下問題,發現:

數字僅僅沒有價值而已,而且數列容量上限就是數字大小的和,,那我們可以使數字的價值等於它本身的大小.這樣兩個模型就變得一模一樣.

然後01揹包問題的解法:

1.表示狀態:

設d[i,j]表示前i個石頭,袋子容量爲j的情況下最多能獲取多少價值. 

2.尋找狀態轉移方程

設第i個石頭的重量爲wi,價值爲vi

dp[i,j]=max(   放第i個石頭可以得到的最大價值,前提是第i個石頭可以放進去    ;

                      不放第i個石頭可以得到的最大價值        )

         =max( dp[i-1,j-wi]+vi   ,     dp[i-1, j]) 

3.優化方程

我們發現,對於狀態的第一維:

第i個狀態只跟第i-1個狀態有關,  顯然我們可以將O(n*n)的空間複雜度壓縮成O(2n)的空間複雜度(兩個數組倒來倒去就行了).

然後方程就砍掉了一維i

方程變爲:  dp[j] = max( dp[j-wi]+vi   ,    dp[j]) ;   但是還是兩層循環,i標只是不出現在方程裏了,該循環還得循環(沒有時間上的優化)

進一步優化:

我們甚至可以不用兩個數組,只用一個數組:

考慮到第二維:  第j狀態只跟第0,1....j-1,j狀態有關,跟比j大的狀態無關.所以滿足所謂的無後效性.

這樣我們可以倒着求dp[j],只用一個數組.

dp[j] = max( dp[j-wi]+vi   ,    dp[j]) ;  //其中i正向循環(這個i的順序其實無所謂),j從大到小循環(必須的)

空間只需要一個一維dp數組就行了.

4.考慮邊界和結果

這個就不說了吧.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章