0-1背包问题(回溯法解决)

 

0-1背包问题(回溯法解决)

给定一个物品集合s={1,2,3,…,n},物品i的重量是wi,其价值是vi,背包的容量为W,即最大载重量不超过W。在限定的总重量W内,我们如何选择物品,才能使得物品的总价值最大。

输入
第一个数据是背包的容量为c(1≤c≤1500),第二个数据是物品的数量为n(1≤n≤50)。接下来n行是物品i的重量是wi,其价值为vi。所有的数据全部为整数,且保证输入数据中物品的总重量大于背包的容量。

输出:对每组测试数据,输出装入背包中物品的最大价值。

令cw(i)表示目前搜索到第i层已经装入背包的物品总重量,即部分解(x1, x2 , …, xi)的重量:

在这里插入图片描述
对于左子树, xi =1 ,其约束函数为:
在这里插入图片描述

若constraint(i)>W,则停止搜索左子树,否则继续搜索。

对于右子树,为了提高搜索效率,采用限界函数Bound(i)剪枝。
令cv(i)表示目前到第i层结点已经装入背包的物品价值:

在这里插入图片描述
令r(i)表示剩余物品的总价值:
在这里插入图片描述

则限界函数Bound(i)为:

在这里插入图片描述

假设当前最优值为bestv,若Bound(i)<bestv,则停止搜索第i层结点及其子树,否则继续搜索。
显然r(i)越小, Bound(i)越小,剪掉的分支就越多(在高层剪枝,剪掉的分支越多。从而能加快搜索速度)。
为了构造更小的r(i) ,将物品以单位重量价值比di=vi/wi递减的顺序进行排列(贪心策略):
d1≥d2≥… ≥dn
对于第i层,背包的剩余容量为W-cw(i),采用贪心算法把剩余的物品放进背包。

在这里插入图片描述

为什么没有按照重量进行排序呢?

0-1背包问题的目标是:
在不超重的前提下计算背包内物品的最大价值
因此,贪心策略是:
按照单位价值由大到小进行处理
物品的价值会对限界函数产生影响
物品的重量会对约束函数产生影响
由于根据物品价值制定贪心策略
所以,通过单位价值排序,加速剪枝

#include<iostream>
#include<algorithm>
using namespace std;
#define NUM 100
int c;			//背包的容量		
int n;			//物品的数量
int cw;			//当前背包内物品的重量
int cv;			//当前背包内物品的总价值
int bestv;		//当前最优价值



//描述每个物品的数据结构
struct Object
{
public:
	int w;		//物品的重量
	int v;		//物品的价值
	double d;	//物品的单位价值
public:
	double getd()
	{
		return d;
	}
};		//物品数组

Object Q[NUM];
void backtrack(int i);
int Bound(int i);
bool cmp(Object, Object);


//物品的单位价值重量比是在输入数据时计算的


int main()
{
	cin >> c >> n;


	for (int i = 0; i < n; i++)
	{
		//物品的单位价值重量比是在输入数据时计算的
		scanf_s("%d%d", &Q[i].w, &Q[i].v);
		Q[i].d = 1.0 * Q[i].v / Q[i].w;

	}

	sort(Q, Q + n, cmp);

	backtrack(1);

	cout << bestv;

}








//以物品单位价值重量比递减排序的因子:
bool cmp(Object a, Object b)
{
	if (a.d>= b.d)
		return true;
	else
		return false;
	
}


void backtrack(int i)
{
	//到达叶子节点时更新最优值
	if (i + 1 > n)
	{
		bestv = cv;
		return;
	}

	//进入左子树搜索
	if (cw + Q[i].w <= c)
	{
		cw += Q[i].w;
		cv += Q[i].v;
		backtrack(i + 1);
		cw -= Q[i].w;
		cv -= Q[i].v;

	}

	//进入右子树搜索
	if (Bound(i + 1) > bestv)
		backtrack(i + 1);
}

int Bound(int i)
{
	int cleft = c - cw;				//背包剩余的容量
	int b = cv;						//上界
	//尽量装满背包
	while (i < n && Q[i].w <= cleft)
	{
		cleft -= Q[i].w;
		b += Q[i].v;
		i++;
	}

	//剩余的部分空间也装满
	if (i < n)
		b += 1.0 * cleft * Q[i].v / Q[i].w;
	return b;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
0-1背包问题是一个经典的组合优化问题,目标是在给定的一组物品中选择一些物品放入背包,使得物品的总重量不超过背包的容量,同时总价值最大化。回溯法是一种解决0-1背包问题的方法。 回溯法的基本思想是通过递归的方式,穷举所有可能的解,并通过剪枝操作来减少不必要的计算。具体步骤如下: 1. 定义一个递归函数backtrack,该函数接受四个参数:当前物品的索引i,当前背包的重量cw,当前背包的价值cv,以及剩余物品的重量和价值数组。 2. 在backtrack函数中,首先判断递归终止条件。如果当前物品的索引i等于物品个数n,或者当前背包的重量cw大于背包容量c,则返回当前背包的价值cv。 3. 如果递归没有终止,那么有两种情况需要考虑: - 选择当前物品放入背包:将当前物品的重量和价值加到cw和cv上,并递归调用backtrack函数,更新当前物品的索引i为i+1。 - 不选择当前物品放入背包:直接递归调用backtrack函数,更新当前物品的索引i为i+1。 4. 在每次递归调用后,比较选择和不选择两种情况的结果,返回价值较大的那个。 5. 最后,调用backtrack函数,传入初始参数i=0,cw=0,cv=0,得到0-1背包问题的最优解和最优值。 下面是使用回溯法解决0-1背包问题的示例代码: ```python def backtrack(i, cw, cv, weight, value, n, c): if i == n or cw == c: return cv if cw + weight[i] <= c: choose = backtrack(i+1, cw+weight[i], cv+value[i], weight, value, n, c) else: choose = 0 not_choose = backtrack(i+1, cw, cv, weight, value, n, c) return max(choose, not_choose) weight = [2, 3, 4, 5] value = [3, 4, 5, 6] n = len(weight) c = 8 max_value = backtrack(0, 0, 0, weight, value, n, c) print("The maximum value is:", max_value) ``` 该代码中,weight和value分别表示物品的重量和价值,n表示物品个数,c表示背包容量。运行代码后,会输出0-1背包问题的最优值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值