用回溯法求解0-1背包问题

代码注释写的很清晰,可以直接看代码来理解

算法思路:

01背包属于找最优解问题,用回溯法需要构造解的子集树。对于每一个物品i,对于该物品只有选与不选2个决策,总共有n个物品,可以顺序依次考虑每个物品,这样就形成了一棵解空间树: 基本思想就是遍历这棵树,以枚举所有情况,最后进行判断,如果重量不超过背包容量,且价值最大的话,该方案就是最后的答案。

在搜索状态空间树时,只要左子节点是可一个可行结点,搜索就进入其左子树。对于右子树时,先计算上界函数,以判断是否将其减去(剪枝)。

上界函数bound():当前价值cw+剩余容量可容纳的最大价值<=当前最优价值bestp。

为了更好地计算和运用上界函数剪枝,选择先将物品按照其单位重量价值从大到小排序,此后就按照顺序考虑各个物品。

代码:

#include <iostream>
using namespace std;
#define maxn 1000 
int n;//物品数量 
int c;//背包容量 
double w[maxn];//存放物品重量的数组 
double v[maxn];//存放物品价值的数组 
int order[maxn];//物品编号1~n
int best_x[maxn];//用于记录回溯过程的最优情况 
double vw[maxn];//物品单位重量价值 
int x[maxn];//记录当前物品是否装入背包 
int bestv;//最优价值 best value
int cw;//当前重量 current weight 
int cv;//当前价值 current value 
void paixu()
{
	for(int i=1;i<=n;++i)
		vw[i]=v[i]/w[i];//单位重量价值
 	for(int i=1;i<=n-1;i++)
    {
        for(int j=i+1;j<=n;j++)
            if(vw[i]<vw[j])//冒泡排序vw[],order[],v[],w[]
        {	double temp;
            temp = vw[i];  //冒泡对vw[]排序
            vw[i]=vw[i];
            vw[j]=temp;
 
            temp=order[i];//冒泡对order[]排序
            order[i]=order[j];
            order[j]=temp;
 
            temp = v[i];//冒泡对v[]排序
            v[i]=v[j];
            v[j]=temp;
 
            temp=w[i];//冒泡对w[]排序
            w[i]=w[j];
            w[j]=temp;
        }
    }
}
int Bound(int i)//限界函数:该函数返回装入所有剩余物品后(不能超过c的前提下)的价值 
{
	int cleft=c-cw;//剩余容量
	int value=cv;
	while(i<=n&&w[i]<=cleft)
	{
		cleft-=w[i];
		value+=v[i];
		++i; 
	 } 
	if(i<=n) value+=v[i]*cleft/w[i];
	return value; 
}
void Backtrack(int i)
{
	if(i>n)//到达根节点且根节点处理完毕
	{	for(int i = 1; i <= n; i++)
            best_x[i] = x[i];   //记录回溯的最优情况
		bestv=cv;//更新最优的价值 
		return;
	}
	else//处理中间过程的节点 
	{
		if(cw+w[i]<=c)//如果满足约束条件,进入左子树(约束条件:不超过容量c) 
		{
			x[i]=1;//用来main函数构造最优解,将物品放入时x[i]=1 
			cw+=w[i];
			cv+=v[i];
			Backtrack(i+1);
			cw-=w[i];
			cv-=v[i];
		}
		if(Bound(i+1)>bestv)//满足限界函数进入右子树 
		{
			x[i]=0;//右子树意味着物品不装入,x[i]=0 
			Backtrack(i+1); 
		}
	} 
}
int main()
{
	bestv=0;cv=0;cw=0;
	cout<<"请输入物品数量:";
	cin>>n;//物品数量
	cout<<"请输入背包容量:";
	cin>>c;//背包容量
	cout<<"请输入所有物品重量:"<<endl;
	for(int i=1;i<=n;++i)
		cin>>w[i];
	cout<<"请输入所有物品价值:"<<endl;	
	for(int i=1;i<=n;++i)
		cin>>v[i];
		
	for(int i=1;i<=n;++i)//物品编号:1~n 
		order[i]=i;
	paixu();
	//将物品按照单位重量价值排序(w,v,order,vw数组都要按照这个排序) 
	Backtrack(1);//从根节点开始回溯 
	cout<<"可装入的最大价值:"<<bestv<<endl;
	cout<<"放入背包的物品编号:" ; 
	for(int i=1;i<=n;++i)
	if(best_x[i]==1) cout<<order[i]<<" ";
	return 0;
	
 } 

运行结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值