
本文首發自「慕課網」,想了解更多IT干貨內容,程序員圈內熱聞,歡迎關注!
作者| 慕課網精英講師 JdreamZhang
假設我們一共有 n 種物品,每種物品 i 的價值為 vi,重量為 wi,我們有一個背包,背包的容量為 c(最多可以放的物品重量不能超過 c),我們需要選擇物品放入背包中,使得背包中選擇的物品中總價值最大,在這里每個物品可以只選擇部分。這便是我們常說的背包問題
(相關資料圖)
背包問題是一種常見的可以用貪心算法進行求解的問題,接下來,就讓我們看看如何利用貪心算法解決背包問題。
首先,這里我們考慮背包的容量為 30,并給出這個問題中我們考慮到的各類物品及對應的重量和價值,如下:
物品 n (i) | 1 | 2 | 3 | 4 | 5 |
重量 w (i) | 10 | 5 | 15 | 10 | 20 |
價值 v (i) | 20 | 30 | 15 | 25 | 10 |
回顧一下我們在貪心算法介紹中提到的,能夠應用貪心算法求解的問題需要滿足兩個條件:最優子結構和貪心選擇,接下來,我們就具體來看看在背包問題中的最優子結構和貪心選擇分別是什么。
首先,如果一個問題的最優解包含其子問題的最優解,則此問題具備最優子結構的性質。問題的最優子結構性質是該問題是否可以用貪心算法求解的關鍵所在。對于背包問題,很顯然它是滿足最優子結構性質的,因為一個容量為 c 的背包問題必然包含容量小于 c 的背包問題的最優解的。
其次,我們需要考慮在背包問題中,我們應該按照什么樣的貪心選擇進行選取。很顯然,如果要使得最終的價值最大,那么必定需要使得選擇的單位重量的物品的價值最大。所以背包問題的貪心選擇策略是:優先選擇單位重量價值最大的物品,當這個物品選擇完之后,繼續選擇其他價值最大的物品。
這里的背包問題可以用貪心算法實現,是因為背包選擇放入的物品可以進行拆分,即并不需要放入整個物品,可以選擇放入部分物品,我們這樣的背包選擇問題為部分背包問題(可以只選擇物品的部分),與之對應的另一種背包問題為 0-1 背包問題,這個時候整個物品不可以拆分,只可以選擇放入或者不放入,0-1 背包問題用貪心算法并不能求得準確的解,需要用動態規劃算法求解。
背包問題求解詳解:
在這里,我們按照優先選擇單位重量價值最大的物品,所以第一步需要計算單位每個物品的單位價值,如下:
unitValue(1) = 20/10 = 2unitValue(2) = 30/5 = 6unitValue(3) = 15/15 = 1unitValue(4) = 25/10 = 2.5unitValue(5) = 10/20 = 0.5代碼塊12345
所以我們有:
unitValue(2) > unitValue(4) > unitValue(1) > unitValue(3) > unitValue(5)代碼塊1
當考慮背包的容量為 30 時, 我們發現可以按照物品的單位價值進行依次放入,直至背包容量放滿或者物品放完為止,放入的過程如下:
物品類型 | 放入重量 | 背包使用容量 | 背包剩余容量 |
2 | 5 | 5 | 25 |
4 | 10 | 15 | 15 |
1 | 10 | 25 | 5 |
3 | 5 | 30 | 0 |
按照如上步驟我們簡單分析了一下背包問題的求解過程,接下來,我們看一下如何用代碼實現背包問題。
在說明求解背包問題的整個過程之后,接下來,我們看看如何用 java 代碼實現背包問題的求解。
import java.util.ArrayList;import java.util.Collections;import java.util.List;public class Knapsack { /** * 物品內部類 */ private static class Item implements Comparable- { int type; double weight; double value; double unitValue; public Item(int type, double weight){ this.type = type; this.weight = weight; } public Item(int type, double weight,double value){ this.type = type; this.weight = weight; this.value = value; this.unitValue = value/weight; } @Override public int compareTo(Item o) { return Double.valueOf(o.unitValue).compareTo(this.unitValue); } } public static void main(String[] args){ //背包容量 double capacity = 30; //物品類型初始化數組 int[] itemType = {1,2,3,4,5}; //物品重量初始化數組 double[] itemWeight = {10,5,15,10,30}; //物品價值初始化數組 double[] itemValue = {20,30,15,25,10}; //初始化物品 List
- itemList = new ArrayList<>(); for(int i=0;i
selectItemList = new ArrayList<>(); double selectCapacity = 0; for(Item item : itemList){ if( (selectCapacity + item.weight) <= capacity){ selectCapacity = selectCapacity + item.weight; Item selectItem = new Item(item.type,item.weight); selectItemList.add(selectItem); }else { Item selectItem = new Item(item.type, capacity-selectCapacity); selectItemList.add(selectItem); break; } } //選擇結果輸出 for (Item item : selectItemList){ System.out.println("選擇了類型:"+ item.type+" 的物品,重量為:"+item.weight); } }}代碼塊1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
運行結果如下:
選擇了類型:2 的物品,重量為:5.0選擇了類型:4 的物品,重量為:10.0選擇了類型:1 的物品,重量為:10.0選擇了類型:3 的物品,重量為:5.0代碼塊1234
代碼中第 10 行至第 31 行定義了物品的一個內部類,用來存儲一個物品的類型、重量、價值、單位重量的價值,并且實現在其中實現了一個對比函數。代碼的第 35 至 42 行對應著開始的背包問題的初始化工作,分別初始化了背包容量、物品類型、物品重量、物品價值。代碼的第 44 行至 51 行將所有物品按照物品內部類的格式加入數組,并且按照物品單位重量的價值進行降序排序。代碼的第 53 行至第 66 行,按照背包問題的貪心選擇方法選擇對應的物品,并記錄選擇的物品類型及重量,放入到選擇的物品列表中 ,代碼的 69 行 71 行輸出相關的物品選擇結果。
本篇文章主要利用了貪心算法處理背包問題,需要大家掌握貪心算法解決背包問題的流程,并且可以自己用代碼實現背包問題的求解。在學習完這篇文章后,我們通過背包問題這一實例介紹了貪心算法的實際應用,相信一定可以幫助大家更好的理解貪心算法。
歡迎關注「慕課網」,發現更多IT圈優質內容,分享干貨知識,幫助你成為更好的程序員!