1. 程式人生 > >01揹包問題演算法解釋與C程式碼實現

01揹包問題演算法解釋與C程式碼實現

題目有N件物品和一個容量為V的揹包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使價值總和最大。基本思路這是最基礎的揹包問題,特點是:每種物品僅有一件,可以選擇放或不放。用子問題定義狀態:即f[i][v]表示前i件物品恰放入一個容量為v的揹包可以獲得的最大價值。則其狀態轉移方程便是:


這個方程非常重要,基本上所有跟揹包相關的問題的方程都是由它衍生出來的。所以有必要將它詳細解釋一下:“將前i件物品放入容量為v的揹包中”這個子問題,若只考慮第i件物品的策略(放或不放),那麼就可以轉化為一個只牽扯前i-1件物品的問題。如果不放第i件物品,那麼問題就轉化為“前i-1件物品放入容量為v的揹包中”,價值為f[i-1][v];如果放第i件物品,那麼問題就轉化為“前i-1件物品放入剩下的容量為v-c[i]的揹包中”,此時能獲得的最大價值就是f[i-1][v-c[i]]再加上通過放入第i件物品獲得的價值w[i]。優化空間複雜度以上方法的時間和空間複雜度均為(V N),其中時間複雜度應該已經不能再優化了,但空間複雜度卻可以優化到(N)1。這個方程非常重要,基本上所有跟揹包相關的問題的方程都是由它衍生出來的。所以有必要將它詳細解釋一下:“將前i件物品放入容量為v的揹包中”這個子問題,若只考慮第i件物品的策略(放或不放),那麼就可以轉化為一個只牽扯前i-1件物品的問題。如果不放第i件物品,那麼問題就轉化為“前i-1件物品放入容量為v的揹包中”,價值為f[i-1][v];如果放第i件物品,那麼問題就轉化為“前i-1件物品放入剩下的容量為v-c[i]的揹包中”,此時能獲得的最大價值就是f[i-1][v-c[i]]再加上通過放入第i件物品獲得的價值w[i]。優化空間複雜度以上方法的時間和空間複雜度均為(V N),其中時間複雜度應該已經不能再優化了,但空間複雜度卻可以優化到(N)。


題目描述:

有編號分別為a,b,c,d,e的五件物品,它們的重量分別是2,2,6,5,4,它們的價值分別是6,3,5,4,6,現在給你個承重為10的揹包,如何讓揹包裡裝入的物品具有最大的價值總和?

name weight value 1 2 3 4 5 6 7 8 9 10
a 2 6 0 6 6 9 9 12 12 15 15 15
b 2 3 0 3 3 6 6 9 9 9 10 11
c 6 5 0 0 0 6 6 6 6 6 10 11
d 5 4 0 0 0 6 6 6 6 6 10 10
e 4 6 0 0 0 6 6 6 6 6 6 6

只要你能通過找規律手工填寫出上面這張表就算理解了01揹包的動態規劃演算法。

首先要明確這張表是至底向上,從左到右生成的。

為了敘述方便,用e2單元格表示e行2列的單元格,這個單元格的意義是用來表示只有物品e時,有個承重為2的揹包,那麼這個揹包的最大價值是0,因為e物品的重量是4,揹包裝不了。

對於d2單元格,表示只有物品e,d時,承重為2的揹包,所能裝入的最大價值,仍然是0,因為物品e,d都不是這個揹包能裝的。

同理,c2=0,b2=3,a2=6。

對於承重為8的揹包,a8=15,是怎麼得出的呢?

根據01揹包的狀態轉換方程,需要考察兩個值,

一個是f[i-1,j],對於這個例子來說就是b8的值9,另一個是f[i-1,j-Wi]+Pi;

在這裡,

 f[i-1,j]表示我有一個承重為8的揹包,當只有物品b,c,d,e四件可選時,這個揹包能裝入的最大價值

f[i-1,j-Wi]表示我有一個承重為6的揹包(等於當前揹包承重減去物品a的重量),當只有物品b,c,d,e四件可選時,這個揹包能裝入的最大價值

f[i-1,j-Wi]就是指單元格b6,值為9,Pi指的是a物品的價值,即6

由於f[i-1,j-Wi]+Pi = 9 + 6 = 15 大於f[i-1,j] = 9,所以物品a應該放入承重為8的揹包


#include <stdio.h>
#define N 7
#define S 15
typedef struct 
{ 
	    int s;
		int n; 
		int job;
}KNAPTP;



int knap(int s,int n);
int w[N+1] = {0,1,4,3,4,5,2,7};
void main()
{ 

		if (knap(S,N)) 

			printf("0K!\n" );
		else 
			printf( "N0!\n" );
}
		
		int knap( int s,int n )
		{ 
				KNAPTP stack[100],x; 
				int top, k, rep; 
				x.s =s; x.n =n; 
				x.job = 0; 
				top = 1; stack[top] = x;
				k = 0;
				
				while ( top>0 && k == 0 )  
				{ 
					x = stack[top];
						rep = 1;
						while ( !k && rep ) 
						{ 
							if( x.s==0 ) k = 1; /*已求得一組解*/ 
								else if ( x.s<0 || x.n<=0 ) rep = 0;
								else { 
									x.s = x.s - w[x.n--] ;
									x.job=1;
								stack[++top] = x;
							} 
						} 
						
						if ( !k ) 
						{ 
							rep = 1;
								while ( top >= 1 && rep)
								{ 
									x = stack[top--]; 
										if ( x.job==1)
										{ 
											x.s+=w[x.n + 1];
											x.job = 2;
											stack[++top] = x; 
											rep = 0 ;
										} 
								} 
						} 
				} 
				if (k)
				{   
					/*輸出一組解*/ 
						while ( top >= 1 ) 
						{ 
							x = stack[top--]; 
							if ( x.job == 1 ) 
								printf ("%d\n",w[x.n+1] );
						} 
				} 
				return k;
		}

相關推薦

01揹包問題演算法解釋C程式碼實現

題目有N件物品和一個容量為V的揹包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使價值總和最大。基本思路這是最基礎的揹包問題,特點是:每種物品僅有一件,可以選擇放或不放。用子問題定義狀態:即f[i][v]表示前i件物品恰放入一個容量為v的揹包可以

編譯原理(九) LR(0)文法分析法(演算法描述和C++程式碼實現)

後期DEBUG發現make_set函式和make_go存在問題,於2015年12月4日更新了程式碼,見諒 概念梳理 最左推導:每一步替換最左邊的非終結符 最右推導:每一步替換最右邊的非終結符,最右推導稱為規範推導 短語:令G是一個文法,S是文法的開始符號

NMS 非極大值抑制的原理C++程式碼實現

原理:對於Bounding Box的列表B及其對應的置信度S,選擇具有最大score的檢測框M,將其從B集合中移除並加入到最終的檢測結果D中.通常將B中剩餘檢測框中與M的IoU大於閾值Nt的框從B中移除.重複這個過程,直到B為空實現過程:就像上面的圖片一樣,定位一個車輛,最後

01揹包問題(用c語言實現)-回溯法求解

回溯法求解01揹包   用回溯法解問題時,應明確定義問題的解空間。問題的解空間至少應包含問題的一個(最優)解。例如,對於有n種可選擇物品的0-1揹包問題,其解空間由長度為n的0-1向量組成。該解空間

各種排序演算法總結及C#程式碼實現

 排序是計算機內經常進行的一種操作,其目的是將一組“無序”的記錄序列調整為“有序”的記錄序列。分內部排序和外部排序。若整個排序過程不需要訪問外存便能完成,則稱此類排序問題為內部排序。反之,若參加排序的記錄數量很大,整個序列的排序過程不可能在記憶體中完成,則稱此類排序問

無監督學習之K-均值演算法分析MATLAB程式碼實現

前言 K-均值是一種無監督的聚類演算法。首先我們要知道什麼是無監督,無監督就是說在資料集中,資料是沒有標籤的。在有監督的資料集中,資料的形式可能是這樣:{(x(1),y(1)),(x(2),y(2)),...,(x(m),y(m))}。而在無監督的資料集中,資

影象去霧之何凱明暗通道先驗去霧演算法原理及c++程式碼實現

http://blog.csdn.net/s12244315/article/details/50292049 何凱明博士,2007年清華大學畢業,2011年香港中文大學博士畢業,可謂是功力深厚,感嘆於國內一些所謂博士的水平,何這樣的博士才可以真正叫做

梅爾頻率倒譜系數(MFCC)的提取過程C++程式碼實現

MFCC引數提取步驟 ——>預加重 ——>分幀 ——>對每一幀加窗 ——>對每一幀補零 ——>各幀訊號的FFT變換及其功率譜 ——>梅爾濾波(通過40個濾波器) ——>取對數 ——>DCT變換 ——>歸一化 1.預加重

base64編解碼原理C程式碼實現

1、base64編碼原理分析: (1)、背景與應用: 可參考部落格:Base64編碼原理與應用 所謂base64就是基於ASCII碼的64個可見字元子集的一種編碼方式。 (2)、編碼原理與核心分析: 上面提到的子集如下所示: A~Z、a~z、0~

01揹包問題 及c++ 程式碼實現

    今天在看july的部落格之時,看到其中一道題目的原理為01揹包問題,就自己溫習了下,寫下今天的學習體會。 本文理論分析參考部落格:http://www.cnblogs.com/qinyg/archive/2012/04/26/2471829.html 問題描述:  

c#程式碼實現排序演算法之歸併排序

歸併排序的平均時間複雜度為O(nlogn),最好時間複雜度為O(nlogn),最壞時間複雜度為O(nlogn),空間複雜度為O(n),是一種穩定的演算法。 1.將待排序序列r(1),r(2),…,r(n)劃分為兩個長度相等的子序列r(1),…r(n/2)和r(n/2+1),…,r

c#程式碼實現排序演算法之快速排序

快速排序的平均時間複雜度為O(nlog2n),最好時間複雜度為O(nlog2n),最壞時間複雜度為O(n²),空間複雜度為O(log2n),是一種不穩定的演算法。 1.劃分:選定一個記錄作為軸值,以軸值為基準將整個序列劃分為兩個子序列r(1)…r(i-1)和r(i+1)…r(n)

c#程式碼實現排序演算法之氣泡排序

氣泡排序的平均時間複雜度為O(n²),最好時間複雜度為O(n),最壞時間複雜度為O(n²),空間複雜度為O(1),是一種穩定的演算法。 1.將整個待排序的記錄序列劃分成有序區和無序區,初始時有序區為空,無序區包括所有待排序的記錄。 2.對無序區從前向後依次比較相鄰記錄,若反序則交

c#程式碼實現排序演算法之選擇排序

選擇排序的平均時間複雜度為O(n²),最好時間複雜度為O(n²),最壞時間複雜度為O(n²),空間複雜度為O(1),是一種不穩定的演算法。 1.將整個記錄序列劃分為有序區和無序區,初始時有序區為空,無序區含有待排序的所有記錄。 2.在無序區查詢值最小的記錄,將它與無序區的第一個記

c#程式碼實現排序演算法之插入排序

插入排序的平均時間複雜度為O(n²),最好時間複雜度為O(n),最壞時間複雜度為O(n²),空間複雜度為O(1),是一種穩定的演算法。 1.將整個待排序的記錄序列劃分成有序區和無序區,初始時有序區為待排序記錄序列的第一個記錄,無序區包括所有剩餘待排序的記錄。 2.將無序區的第一個

演算法設計之Project Euler 11~20 (python3.6版C++版實現)(未完待更)

一、Project Euler 11:Largest product in a grid Largest product in a grid In the 20×20 grid below, four numbers along a diagonal line have been

買什麼資料結構演算法,這裡有:動態圖解十大經典排序演算法(含JAVA程式碼實現

上篇的動圖資料結構反響不錯,這次來個動圖排序演算法大全。資料結構與演算法,齊了。 幾張動態圖捋清Java常用資料結構及其設計原理 本文將採取動態圖+文字描述+正確的java程式碼實現來講解以下十大排序演算法: 氣泡排序 選擇排序 插入排序 希爾排序

模擬退火演算法C語言實現(TSP問題)

1簡介: 模擬退火來自冶金學的專有名詞退火。退火是將材料加熱後再經特定速率冷卻,目的是增大晶粒的體積,並且減少晶格中的缺陷。材料中的原子原來會停留在使內能有區域性最小值的位置,加熱使能量變大,原子會離開原來位置,而隨機在其他位置中移動。退火冷卻時速度較慢,使得原子有較多可能

歸併排序演算法 C程式碼實現

合併排序(MERGE SORT)是又一類不同的排序方法,合併的含義就是將兩個或兩個以上的有序資料序列合併成一個新的有序資料序列,因此它又叫歸併演算法。它的基本思想就是假設陣列A有N個元素,那麼可以看成陣列A是又N個有序的子序列組成,每個子序列的長度為1,然後再兩兩合併,得到

圖的鄰接矩陣表示最短路徑演算法( Dijkstra )程式碼實現

#include <stdio.h> #define MAX_VERTEX_NUM 20 //最大頂點個數 typedef int VRTYPE, InfoType; typedef enum {DG, DN, UDG, UD