回溯法解决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)。

你可能感兴趣的:(回溯法解决01背包问题)