1. 程式人生 > >01揹包問題的分析與優化

01揹包問題的分析與優化

揹包問題是動態規劃的經典問題,可以分為多個子結構,如,

只使用第1個物品在揹包容量為1的情況下揹包所能裝的最大價值:為V[1][1]

只使用第1個物品在揹包容量為2的情況下揹包所能裝的最大價值:為V[1][2]

只使用第1個物品在揹包容量為j的情況下揹包所能裝的最大價值:為V[1][j]

只使用第1,2個物品在揹包容量為j的情況下揹包所能裝的最大價值:為V[2][j]

只使用第1,2,3...i 個物品在揹包容量為j的情況下揹包所能裝的最大價值:為V[i][j]

當使用第 1,2,3,i-1個物品在揹包剩餘容量為j的情況下,在選第i個物品的時候,如果第i個物品重量大於揹包容量,則第i個物品不能放進去 V[i][j] = V[i-1][j];

否則的話,就可以選擇放進去或者不放進去,就要看哪種價值最大 V[i][j] = max(V[i-1][j], V[i-1][j - weight[i]] + value[i]) 

程式碼如下:

#define M 8 //揹包容量
#define N 6	// 物品個數
#define max(x,y) x > y ? x : y	//定義比大小的巨集
int V[N + 1][M + 1] = {0};	//定義一個二維陣列儲存所有狀態的揹包對應價值,因為V[0][]和V[][0]都必須初始為0所以下標0不能作為有效資料,要多設定一個下標資料
int weight[N+1] ={0,1,2,3,4,5,6};	//同上weight[]和value[]的0下標位都初始化為0,不放資料,這個不是必須的,但是方便理解
int value[N+1] = {0,2,3,4,5,6,7};
void package01()
{
	for(int i = 1; i <= N; i++)		//i對應第i個物品,i的迴圈放外面,這個是關鍵點,一行一行的寫入資料。因為V[i][]對應資料是由V[i-1][]決定的,因此上一行資料的完整才能保證下一行資料完整
	{
		for(int j = 1; j <= M; j++)	//j對應當前揹包能承受重量為j
		{
			if(j >= weight[i])	
			{
				V[i][j] = max(V[i - 1][j], V[i - 1][j - weight[i]] + value[i]);
			}
			else
			{
				V[i][j] = V[i - 1][j];
			}
		}
	}
}
其實陣列每次寫入一行資料都是根據上一行的資料進行寫入,不需要用二維陣列,可以用一維陣列,每次根據自身進行更新即可,重點是[i-1][j - weight[i]]這個資料的意思是我要在表中上一行找比j小的資料,那換成一維的話,我找的是自身(只有一行,沒有上一行)j左邊的資料,這個資料必須保證是上一次(i-1)寫入的,肯定不能從左向右寫入資料,那樣的話[i-1][j - weight[i]] 對應的就是這一次(i)寫入的,上一次資料沒有利用就丟失了,所以j迴圈應該從M到1,從右向左向一維陣列寫入資料。

程式碼如下;

void package01EX()
{
	for(int i = 1; i <= N; i++)
	{
		for (int j = M; j >= 1; j--)
		{
			if (j >= weight[i])
			{
				V[j] = max(V[j], V[j - weight[i]] + value[i]);
			}
		}
	}
}

其實剛開始還碰到一個頭疼的問題,選擇第i個物品,到底是放還是不放的時候,總覺得放了才好,覺得前面的選項都確定了,錯就錯在前面的選項並沒有確定,可以親自填一下表,就是根據那個二維陣列來填表,先初始化為0,動手填了一遍就發現其實當選第i個物品的時候,前面的各個物品選擇根本沒有確定,二維陣列已經列出了前面可選擇有的可能性比如試探性的選擇,放第i個物品,好那麼j減去weight[i],i減去1,看看上一行資料其實是偏左方對應的那個V是多少,然後加上value[i]對應的就是放這個物品對應的價值,如果不放呢?那就直接等於它頭上的那個V了,也就是V[i-1][j],就意味著不放,我的最後一步是從V[i-1][j]過渡來的,放,就是從頭上偏左的那一步過渡來的,然後前面的那兩步路又是從他們的前面過渡來的,一步一步填表得到的,因此放和不放對應的前面的選擇是不一樣的,不是一條路下來的,放了容量就減少了,往上一行找資料向左找,不放就直接頭上找,左邊的容積肯定比右邊小,那麼代價就是上一行左邊的資料很有可能比右邊的資料小,不可能比右邊大,如果這個小點的資料加上第i個物品價值比頭上的資料大,那麼就意味著,放進去價值大,否則相反。也就是說我現在這一步第i個物品放與不放,就決定著上一步的容積有多少,反正容積多少的都列出來了,自己找就好了,而上一步的資訊又給我提供了參考,可以分析出放與不放哪個價值大,所謂是相互關聯缺一不可。