完全背包問題及Python代碼實現

上一節中,我們介紹了0-1背包問題,接下來,我們來學習一下背包問題的其他變形問題,今天要學習的是完全背包問題。

1、簡介

有 N 種物品和一個容量為 W 的背包,每種物品都有無限件可用。第 i 種物品的重量是 w[i],價值是 v[i]。求解將哪些物品裝入背包可使這些物品的重量總和不超過背包容量,且價值總和最大。可以看到,與0-1背包問題不同的地方時,完全背包問題允許一件物品無限次的出現。

2、基本思路

這個問題非常類似于 0-1 背包問題,所不同的是每種物品有無限件。也就是從每 種物品的角度考慮,與它相關的策略已并非取或不取兩種,而是有取 0 件、取 1 件、取 2 件??等很多種。如果仍然按照解 01 背包時的思路,令 f[i][w]表示 前 i 種物品恰放入一個容量為 w 的背包的最大權值。仍然可以按照每種物品不同 的策略寫出狀態轉移方程,像這樣:
f[i][w]=max{f[i-1][w-kw[i]]+kw[i]|0<=kw[i]<=w}
這跟 0-1 背包問題一樣有 O(N
W)個狀態需要求解,但求解每個狀態的時間已經不 是常數了,求解狀態 f[i][v]的時間是 O(w/w[i]),總的復雜度是超過 O(WN)的。
將 0-1 背包問題的基本思路加以改進,得到了這樣一個清晰的方法。這說明 0-1 背包問題的方程的確是很重要,可以推及其它類型的背包問題。但我們還是試圖 改進這個復雜度。

3、簡單優化

完全背包問題有一個很簡單有效的優化,是這樣的:若兩件物品 i、j 滿足 w[i]<=w[j]且 v[i]>=v[j],則將物品 j 去掉,不用考慮。這個優化的正確性顯 然:任何情況下都可將價值小費用高得 j 換成物美價廉的 i,得到至少不會更差 的方案。對于隨機生成的數據,這個方法往往會大大減少物品的件數,從而加快 速度。然而這個并不能改善最壞情況的復雜度,因為有可能特別設計的數據可以 一件物品也去不掉。
這個優化可以簡單的 O(N^2)地實現,一般都可以承受。另外,針對背包問題而 言,比較不錯的一種方法是:首先將費用大于 V 的物品去掉,然后使用類似計數 排序的做法,計算出費用相同的物品中價值最高的是哪個,可以 O(W+N)地完成 這個優化.

4、轉化為0-1背包問題

既然 0-1 背包問題是最基本的背包問題,那么我們可以考慮把完全背包問題轉化 為 0-1 背包問題來解。最簡單的想法是,考慮到第 i 種物品最多選 W/w[i]件,于 是可以把第 i 種物品轉化為 W/w[i]件費用及價值均不變的物品,然后求解這個 0-1 背包問題。這樣完全沒有改進基本思路的時間復雜度,但這畢竟給了我們將 完全背包問題轉化為 0-1 背包問題的思路:將一種物品拆成多件物品。
更高效的轉化方法是:把第 i 種物品拆成費用為 w[i]2^k、價值為 v[i]2^k 的 若干件物品,其中 k 滿足 w[i]*2^k<=W。這是二進制的思想,因為不管最優策略 選幾件第 i 種物品,總可以表示成若干個 2^k 件物品的和。這樣把每種物品拆成 O(log(W/w[i]))件物品,是一個很大的改進。

但我們有更優的 O(VN)的算法。

5、O(VN)的算法

這個算法使用一維數組,先看偽代碼:

   for i=1..N
       for w=0..W
f[w]=max{f[w],f[w-cost]+weight}

你會發現,這個偽代碼與0-1背包問題只有 的循環次序不同而已。為什么這樣 一改就可行呢?首先想想為什么 0-1背包問題中要按照 w=W..0 的逆序來循環。這是因為 要保證第 i 次循環中的狀態 f[i][w]是由狀態 f[i-1][w-w[i]]遞推而來。換句話 說,這正是為了保證每件物品只選一次,保證在考慮“選入第 i 件物品”這件策 略時,依據的是一個絕無已經選入第 i 件物品的子結果 f[i-1][w-w[i]]。而現 在完全背包的特點恰是每種物品可選無限件,所以在考慮“加選一件第 i 種物 品”這種策略時,卻正需要一個可能已選入第 i 種物品的子結果 f[i][w-w[i]], 所以就可以并且必須采用 w=0..W 的順序循環。這就是這個簡單的程序為何成立 的道理。

6、完全背包問題Python實現

基于上面的思路,完全背包問題Python實現代碼如下:

def solve3(vlist,wlist,totalWeight,totalLength):
    """完全背包問題"""
    resArr = np.zeros((totalWeight)+1,dtype=np.int32)
    for i in range(1,totalLength+1):
        for j in range(1,totalWeight+1):
            if wlist[i] <= j:
                resArr[j] = max(resArr[j],resArr[j-wlist[i]]+vlist[i])
    return resArr[-1]

if __name__ == '__main__':
    v = [0,60,100,120]
    w = [0,10,20,30]
    weight = 50
    n = 3
    result = solve3(v,w,weight,n)
    print(result)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容