1. 程式人生 > >【演算法】----貪心演算法(揹包問題)

【演算法】----貪心演算法(揹包問題)

    【前言:】

    上一篇部落格從概念上說了一下貪心演算法,這次我們通過一個例項,來進一步幫助大家理解貪心演算法。

    一、【經典例項:】(揹包問題)

    給定n個物品和一個容量為C的揹包,物品i的重量是Wi,其價值為Vi,揹包問題是如何選擇入揹包的物品,使得裝入揹包的物品的總價值最大,注意和0/1揹包的區別,在揹包問題中可以將物品的一部分裝入揹包,但不能重複裝入。

    二、【想法:】

    用貪心法求解揹包問題的關鍵是如何選定貪心策略,使得按照一定的順序選擇每個物品,並儘可能的裝入揹包,知道揹包裝滿。至少有三種看似合適的貪心策略。

  1. 選擇價值最大的物品,因為這可以儘可能快的增加揹包的總價值,但是,雖然每一步選擇獲得了揹包價值的極大增長,但揹包容量卻可能消耗的太快,使得裝入揹包的物品個數減少,從而不能保證目標函式達到最大。
  2. 選擇重量最輕的物品,因為這可以裝入儘可能多的物品,從而增加揹包的總價值。但是,雖然每一步選擇使揹包的容量消耗的慢了,但揹包的價值卻沒能保證迅速的增長,從而不能保證目標函式達到最大。
  3. 以上兩種貪心策略或者只考慮揹包價值的增長,或者只考慮揹包容量的消耗,而為了求得揹包問題的最優解,需要在揹包價值增長和揹包容量消耗二者之間尋找平衡。正確的貪心策略是選擇單位重量價值最大的物品。

    例如:有三個物品,其重量分別為{20,30,10},價值分別為{60,120,50},揹包的容量為50,應用三種貪心策略裝入揹包的物品和獲得的價值如下圖所示:

        

     三、【演算法:】

    設揹包容量為C,共有n個物品,物品重量存放在陣列W[n]中,價值存放在陣列V[n]中,問題的解存放在陣列X[n]中,貪心法求解揹包問題的演算法如下:

演算法:貪心法求解揹包問題

輸入:揹包的容量C,物品重量W[n],物品價值V[n]

輸出:陣列X[n]

  1. 改變陣列WV的排列順序,使其按單位重量價值V[i]/W[i]降序排列;
  2. 將陣列X[n]初始化為0
  3. i=0
  4. 迴圈直到(W[i]>C

    4.1   將第i個物品放入揹包:X[i]=1;

    4.2    C=C-W[i];

    4.3    i++;

  1. X[i]=C/W[i]

     四、【演算法分析:】

    演算法的時間主要消耗在將各種物品按照單位重量的價值從大到小的排序,因此,其時間複雜性為O(nlog2n)

    揹包問題與0/1揹包問題類似,所不同的是在選擇物品i(1)裝入揹包時。可以選擇一部分,而不一定要全部裝入揹包。揹包問題可以用貪心法求解,而0/1揹包問題卻不能用貪心法求解,下圖給出了一個貪心法求解0/1揹包問題的示例。從下圖可以看出,對於0/1揹包問題,貪心法之所以不能得到最優解,是由於物品不允許分割,因此,無法保證最終能將揹包裝滿,部分閒置的揹包容量使揹包的單位重量價值降低了

。事實上,在考慮0/1揹包問題時,應比較選擇該物品和不選擇該物品所導致的方案,然後再做出最優選擇,由此匯出許多相互重疊的子問題,所以,0/1揹包問題合適用動態規劃法求解。

        

     五、【演算法實現:】

    實現函式KnapSacks實現貪心法求解揹包問題,簡單起見,假設物品已按單位重量降序排列,演算法C++語言描述如下:

int KnapSack (int W[],int V[],int N,int C)
{
	double X[10]={0};
	int maxValue=0;
	for(int i=0;W[i]<C;i++)
	{
		X[i]=1;
		maxValue+=V[i];
		C=C-W[i];
		
	X[i]=(double)C/W[i];
	maxValue+=X[i]*V[i];
	return maxValue;
	}
}


     六、【總結:】

    貪心法並不是從整體最優考慮,它所做出的選擇只是在某種意義上的區域性最優。貪心演算法對於大部分的優化問題都能產生最優解,但不能總獲得整體最優解,通常可以獲得近似最優解。