算法分析与设计--0/1背包问题(回溯法)

本文介绍了一种解决0/1背包问题的方法——回溯法,并通过实例演示了如何利用该算法来实现最优解。文章首先定义了问题背景,接着详细解释了回溯算法的具体步骤,包括排序、计算上界等关键环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法分析与设计--0/1背包问题(回溯法)
问题描述:

给定n种物品和一个容量为C的背包,物品i的重量是wi,其价值为vi,0/1背包问题是如何选择装入背包的物品(物品不可分割),使得装入背包中物品的总价值最大?


#include<iostream>
using namespace std;

const int n = 3;		//物品个数
const int C = 25;		//背包容量
int bestP = 0;			//最大价值
int	w[n] = {20,15, 10};	//物品重量
int	p[n] = {20,30, 25};	//物品价值
int cp = 0;				//当前背包价值
int cw = 0;				//当前背包重量



//w[n]存储物品的重量,p[n]存储价值
//这个方法用来排序,单位价值由大到小
void sortValue(int w[], int p[],int n)
{
	int index;
	int temp;	//暂存变量
	for(int i = 0; i < n - 1; i++)
	{	
		index = i;
		for(int j = i + 1; j < n; j++)
		{
			//单位重量价值的比较
			if((p[j] / w[j]) > (p[index] / w[index]))
			{
				index = j;
			}
		}
		if(index != i) //交换记录,同时更新w和p数组
		{
			//交换价值记录
			temp = p[index];
			p[index] = p[i];
			p[i] = temp;
			//交换重量记录
			temp = w[index];
			w[index] = w[i];
			w[i] = temp;
		}
	}
}


int Bound(int i)
{
    //计算节点所相应价值的上界
    int cleft = C - cw;   //剩余容量
    int b = cp;
    //以物品单位重量价值递减顺序装入物品
    while(i < n && w[i] <= cleft)
    {
        cleft -= w[i];
        b += p[i];
        i++;
    }
    if(i <= n)
    {
        b += p[i] * cleft / w[i];
    }
    return b;
}

//回溯求解0/1背包,寻找可行解
void BackTrack(int i)
{

	if(i > n - 1)
	{
		bestP = cp;
		return;
	}
	if(cw + w[i] <= C)	//当前结点加上下一个结点的重量小于或等于总的背包容量
	{					//进入左子树,也就是说w[i]被装入背包
		cw = cw + w[i];	//背包当前的重量
		cp = cp + p[i];	//当前获得的价值
		BackTrack(i + 1);
		//回溯,进入右子树
		cw = cw - w[i];
		cp = cp - p[i];
	}
	if(Bound(i+1) > bestP)
	{
		BackTrack(i + 1);
	}
}


void putPackage(int w[],int p[])
{
	BackTrack(0);	//从根结点开始
	cout<<"背包的最大价值:"<<bestP<<endl;
}

int main()
{
	for(int i = 0; i < n; i++)
	{
		cout<<w[i]<<" ";
	}
	cout<<endl;
	for(int i = 0; i < n; i++)
	{
		cout<<p[i]<<" ";
	}
	cout<<endl;
	putPackage(w,p);
	system("pause");
	return 0;
}


回溯法0_1背包问题时,会用到状态空间树。在搜索状态空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树。当右子树有可能包含最优解时才进入右子树搜索,否则将右子树剪去。设r是当前剩余物品价值总和;cp是当前价值;bestp是当前最优价值。当cp+r≤bestp时,可剪去右子树。计算右子树中解的上界可以用的方法是将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。由此得到的价值是右子树中解的上界,用此值来剪枝。 为了便于计算上界,可先将物品依其单位重量价值从大到小排序,此后只要顺序考察各物品即可。在实现时,由MaxBoundary函数计算当前结点处的上界。它是类Knap的私有成员。Knap的其他成员记录了解空间树种的节点信息,以减少函数参数的传递以及递归调用时所需要的栈空间。在解空间树的当前扩展结点处,仅当要进入右子树时才计算上界函数MaxBoundary,以判断是否可以将右子树减去。进入左子树时不需要计算上界,因为其上界父结点的上界相同。 在调用函数Knapsack之前,需要先将各物品依其单位重量价值从达到小排序。为此目的,我们定义了类Objiect。其中,运算符通常的定义相反,其目的是为了方便调用已有的排序算法。在通常情况下,排序算法将待排序元素从小到大排序。 在搜索状态空间树时,由函数Backtrack控制。在函数中是利用递归调用的方法实现了空间树的搜索
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值