1. 程式人生 > >回溯法解決01揹包問題

回溯法解決01揹包問題

回溯法

回溯法是一種非常有效的方法,有“通用的解題法”之稱。它有點像窮舉法,但是更帶有跳躍性和系統性,他可以系統性的搜尋一個問題的所有的解和任一解。回溯法採用的是深度優先策略。

回溯法在確定瞭解空間的結構後,從根結點出發,以深度優先的方式搜尋整個解空間,此時根結點成為一個活結點,並且成為當前的擴充套件結點。每次都從擴充套件結點向縱向搜尋新的結點,當演算法搜尋到瞭解空間的任一結點,先判斷該結點是否肯定不包含問題的解(是否還能或者還有必要繼續往下搜尋),如果確定不包含問題的解,就逐層回溯;否則,進入子樹,繼續按照深度優先的策略進行搜尋。當回溯到根結點時,說明搜尋結束了,此時已經得到了一系列的解,根據需要選擇其中的一個或者多個解即可。

回溯法解決問題一般分為三個步驟;

(1)針對所給問題,定義問題的解空間;

(2)確定易於搜尋的解空間結構;

(3)以深度優先的方式搜尋解空間。

回溯法的典型例項——0-1揹包問題
為了方便理解回溯法運算的流程,以0-1揹包問題為例進行分析;

問題:
給定n種物品和一揹包。物品i的重量是wi,其價值為vi,揹包的容量為C。問:應如何選擇裝入揹包的物品,使得裝入揹包中物品的總價值最大?

首先考慮貪心法,為了得到最大的價值,將所有物品按照單位價值(Vi/Wi)降序排列(例如採用希爾排序,時間複雜度為),在放入物品時優先考慮單位價值更高的物品。在搜尋到空間樹中的某個結點P時,已經確定了P及其前面的結點取值,進而判斷從P繼續擴充套件下去是否獲得更大的價值,如果不能,該結點無需擴充套件,可以進行回溯了。下面的函式結合了貪心法判斷從某一點擴充套件開去可能獲得的最大的價值。

int Bound(int *Values, int *Weights,int n,int maxWeight,int num,int current_Weight,int current_profit)
{
	int i = num + 1;
	for (; i < n; i++)
	{
		if (current_Weight + Weights[i] < maxWeight)
		{
			current_profit += Values[i];
			current_Weight += Weights[i];
		}
		else
		{
			current_profit +
= (Values[i] / Weights[i])*(maxWeight - current_Weight); current_Weight = maxWeight; return current_profit; } } return current_profit; } int *Knapsack(int *Values,int *Weights,int n,int maxWeight) { int *X = new int[n]; int *Y = new int[n]; int Weight = 0; int Profit = 0; int current_weight=0, current_profit=0; int i = 0; while (1) { while (i<n&&t_weight + Weights[i] <= maxWeight) { X[i] = SELECT; current_profit += Values[i]; current_weight += Weights[i]; i++; } //---上面的迴圈中,如果是由於i=n結束的,那麼說明深度搜索已經搜尋到了最底層 if (i >= n) { Weight = current_weight; Profit = current_profit; i = n; for (int j = 0; j < n; j++) //------------把陣列X挪給Y; { Y[j] = X[j]; } } //否則就是由於第i個物品在當前情況下無法放入揹包 else { X[i] = UNSELECT; } while (Bound(Values, Weights, n, maxWeight, i, current_weight, current_profit) <= Profit)//如果不可能獲得更大的價值,那麼這個點就不需要進行擴充套件了; { while (i != 0 && X[i] != SELECT)//進行回溯 { i--; } if (i == 0) //當回溯到i=0時候,所有情況都遍歷了 { return Y; } X[i] = UNSELECT; current_profit -= Values[i]; current_weight -= Weights[i]; } i++; } }

在最壞的情況下,所搜尋的結果是一個滿二叉樹,此時相當於採用的就是窮舉法,時間複雜度為O(2^n),而每次決定是否要講n個物品放入揹包都要進行比較,這一步的時間複雜度為n,所以最壞情況下時間複雜度為O(n*2 ^n)。