1. 程式人生 > >動態規劃法解決0/1揹包問題詳解

動態規劃法解決0/1揹包問題詳解

是什麼

         動態規劃(dynamic programming)是求解決策過程最優化的數學方法,把多階段過程轉換為一系列單階段問題,利用各階段之間的關係,逐個求解,創立了解決這類過程優化問題的新方法。

基本思想

          將待求解的問題分解成若干個子問題,先求解子問題,然後從這些子問題的解得到原問題的解。

求解步驟

  1. 找出最優解的性質,刻畫出結構特徵
  2. 遞迴的定義最優解的值
  3. 自底向上的方式計算最優值(例如0/1揹包問題將價值從低到高排序,再依次像揹包中放入,調整)
  4. 根據最優值構造最優解(求出哪些物品放在了揹包中,標記為1,否則標記為0)

0-1揹包問題

問題描述

         現在有5個物品,第i個物品的價值為vi,重量為wi,揹包的容量為W,其中的引數都為非負數,W=17。求解如何放物品使得揹包的總價值最大。假設我們將物品的價值從低到高排好序如下表:
  
物品的價值和重量
物品編號 1 2 3 4 5
價值v 4 5 10 11 13
重量w 3 4 7 8 9



問題分析

  • 嘗試將1號物品放入揹包
            1號物品重量為3,我們先將揹包分成從0到17,共18份,當揹包容量是0時,物品1無法放入揹包,所以最大價值為0,依次類推,當揹包的容量為3時,剛好可以將1號物品放入揹包,則此時揹包中最大價值為4(即為物品1的價值,位置為下表格標紅處),揹包容量依次增大到17,揹包中還是盛放1號物品,所以,將1號物品放入揹包的最大價值為4。
動態規劃法示例
 I\W   0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17
1 0 0 0 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
  • 嘗試將2號物品放入揹包
            2號物品的重量是4,價值為5,當揹包的容量為4時,可以考慮放入物品2或者物品1,因為物品2的價值>物品1的價值,所以將物品2放入揹包,此時揹包的最大價值為5(如下表格中第三行第一個標紅的位置),揹包容量逐漸增大,當揹包容量為7時,可以將物品1和物品2都放入揹包,此時揹包內最大價值為9(物品1價值+物品2價值),揹包容量遞增至17,都是放入物品1和物品2,所以結果是最大價值為9。
動態規劃法示例
I\W 0 1 2 3 4 5 6 7 8 9 10 11 12 13
14 15 16 17
1 0 0 0 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
2 0 0 0 4 5 5 5 9 9 9 9 9 9 9 9 9 9 9
  • 嘗試將3號物品放入揹包
            3號物品的重量為7,價值為10,揹包容量為0~6時,最大價值和上一行(表格第三行)相同,當揹包容量為7時物品3的價值大於上一行時的價值,需要調整,直至揹包容量為10時,揹包中可放入物品1和物品3,且兩者價值之和大於當前最大價值,需要調整為4+10=14,當揹包容量增至14時可以放入物品1、物品2和物品3,且三者價值之和大於當前最大價值,所以調整為4+5+10=19,揹包容量達到17時仍然放這三個物品,所以結果為當前最大價值是19.
動態規劃法示例
I\W 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
1 0 0 0 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
2 0 0 0 4 5 5 5 9 9 9 9 9 9 9 9 9 9 9
3 0 0 0 4 5 5 5 10 10 10 14 14 14 14 19 19 19 19
  • 嘗試將4號物品放入揹包
             4號物品的重量為8,價值為11,在揹包容量為0~7時,揹包內的最大價值與上一行相同,容量增加到8時可嘗試將物品4放入,物品4的價值大於當前最大價值,則條換為11,當揹包容量增至11時刻嘗試放入物品1和物品4,且價值之和大於當前最大價值,則調整為15,揹包容量為12時刻嘗試放入物品2和物品4,且價值之和大於當前最大價值,則調整為16,揹包容量增至15時可嘗試放入物品3和物品4,且價值之和大於當前最大價值,則調整為21,直至揹包容量增加至17時一直存放物品3和物品4為最大價值,則結果為當前最大價值是21.
動態規劃法示例
I\W 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
1 0 0 0 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
2 0 0 0 4 5 5 5 9 9 9 9 9 9 9 9 9 9 9
3 0 0 0 4 5 5 5 10 10 10 14 14 14 14 19 19 19 19
4 0 0 0 4 5 5 5 10 11 11 14 15 16 16 19 21 21 21
  • 嘗試將5號物品放入揹包
            物品5的重量為9,價值為13,則揹包容量在0~8時最大價值和上一行相同,揹包容量為9時刻考慮放入物品5,且物品5的價值比當前價值要大,所以進行調整,當前最大價值為13,揹包容量增至12時刻考慮放入物品1和物品5,且價值之和大於當前最大價值,調整,當前最大價值為17,揹包容量為13時,考慮放入物品2和物品5,且價值之和大於當前最大價值,調整,當前最大價值為18,揹包容量增至16時考慮放入物品3和物品5,且價值之和大於當前最大價值,調整,當前最大價值為23,揹包容量增至17時,考慮放入物品4和物品5,價值之和大於當前最大價值,調整,當前最大價值為24,則結果為當前最大價值為24.
動態規劃法示例
I\W 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
1 0 0 0 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
2 0 0 0 4 5 5 5 9 9 9 9 9 9 9 9 9 9 9
3 0 0 0 4 5 5 5 10 10 10 14 14 14 14 19 19 19 19
4 0 0 0 4 5 5 5 10 11 11 14 15 16 16 19 21 21 21
5 0 0 0 4 5 5 5 10 11 13 14 15 17 18 19 21 23 24

程式碼示例

  • 遞迴定義最優解的值
                      
  • 計算揹包最優解的值
int **KnasackDP(int n,int* Weights,float* Values){
	int i,w;	/*物品編號和揹包剩餘容量*/
	
	/*為二維陣列申請空間*/
	int** c=(int**)malloc(sizeof(int*)*(n+1));
	
	/*宣告一個一維陣列存放物品*/
	for(i=0;i<=n;i++)
		c[i]=(int*)malloc(sizeof(int)*(W+1));
		
	/*初始化二維陣列*/
	for(w=0;w<=W;w++)
	/*0個物品放入揹包中,價值為0*/
		c[0][w]=0;
		
	for(i=1;i<=n;i++){
	/*i個物品放到容量為0的揹包中,價值為0*/
		c[i][0]=0;
		
		for(w=1;w<=W;w++){
			if(Weights[i-1]<=w){	/*揹包剩餘容量大於物品重量*/
				/*將當前物品放入揹包的最大價值>不放的最大價值*/
				if(Weights[i-1]+c[i-1][w-Weights[i-1]]>c[i-1][w])
				{
					/*重量為w的揹包中放入該物品*/
					c[i][w]=Values[i-1]+c[i-1][w-Weights[i-1]];
				}
				else{
					/*重量為w的揹包中不放入該物品*/
					c[i][w]=c[i-1][w];
				}
		    }
		    else
				/*若物品重量不小於揹包容量w,則不放入該物品
				c[i][w]=c[i-1][w];
		}
	}
	/*返回最優值*/
	return c;
}
  • 構造最優解
void OutputKnapsackDP(int n,int W,int *Weights,float *Values,int **c){
	int x[n];
	int i;
	for(i=n;i>1;i--){
		
		if(c[i][W]==c[i-1][W])
		/*物品i沒有放入揹包,存0*/
			x[i-1]=0;
		else{
		/*物品i放入揹包,存1*/
			x[i-1]=1;W=W-Weights[i-1];
			}
	}
	
	if(c[1][W]==0)
	/*第一個物品不放入揹包*/
		x[0]=0;
	else
	/*第一個物品放入揹包*/
		x[0]=1;
		
	for(i=0;i<n;i++)
		if(x[i]==1)
		/*打印出放入揹包的物品重量和對應價值*/
			printf("Weigh:%d,Value:%f\n",Weights[i],Values[i]);
}

總結

1.每一步的解為當前的最優解        2.存放物品時不存在不完整的情況,只能是將整個物品放入揹包或不放入揹包兩種情況        3.求出最優解夠需要構造最優解,判斷出都是哪些物品放進了揹包