抽象出來大概是這樣一個題:
給出一個數組,要求分成兩份,要求兩份和的差值最小.
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.考慮邊界和結果
這個就不說了吧.