0-1背包问题(回溯法+非递归实现)

参考https://blog.csdn.net/peachhhh/article/details/111475680

#include <iostream>
#include <algorithm>

using namespace std;

int n;			//物品数量	
int c;			//背包容量 
int cw;			//当前背包重量 
int cp;			//当前背包装入物品价值 
 
int bestp;		//装包最大价值 
int bestw;		//最大价值时的背包重量 
 
struct object{
	char id;	//物品编号 
	int p;		//物品价值 
	int w;		//物品重量 
	double pw;	//单位重量价值 
	bool flag;
}objects[100];
int cnt[100];

//上界函数 
int Bound(int i)
{
	int cleft = c - cw;		//当前背包剩余容量 
	double b = cp;			
	while(i < n && objects[i].w <= cleft)
	{
		cleft -= objects[i].w;
		b += objects[i].p;
		i++;
	}
	if(i < n)
		b += (1.0 * cleft) / objects[i].w * objects[i].p;
	return b;
}
 
bool cmp(struct object a, struct object b)
{
	return a.pw > b.pw;
}
 
void Solution(int i)
{
	while(i >= 0)//当层数i < 0时退出根节点退出循环 
	{
		/* 
		cnt数组存储访问该层节点的次数:
		当cnt[i]为0时即第一次访问,进入左子树;
		当cnt[i]为1时即第二次访问,进入右子树
		当cnt[i]大于1时说明该节点的左右子树均被访问,退出该层节点i--
		*/
		if(cnt[i] <= 1)
		{
			for(int j = cnt[i]; j <= 1; j++)//即 cnt[i]从0 ~ 1依次查找左右子树
			{
				if(i == n)//搜到叶子结点,更新bestp
				{
					bestp = cp;
					bestw = cw;
					i--;
				}
				int child = 1 - cnt[i];
				/*
				当cnt[i]为0时,child为1即访问左子树
	            当cnt[i]为1时,child为0即访问右子树
				*/
				if(child)
				{	//满足约束函数进入左子树 
					if(cw + objects[i].w <= c)
					{
						objects[i].flag = true;
						cw += objects[i].w;
						cp += objects[i].p;
						cnt[i]++;
						i++;
					}
					else	
						cnt[i]++;//即使无法访问左子树,即以该结点为根的子树被剪掉,但这也说明当前扩展结点的左儿子已被访问 
				}
				else
				{
					//满足限界函数进入右子树 
					if(Bound(i + 1) > bestp)
					{
						cnt[i]++;
						i++;
					}
					else 
						cnt[i]++;//即使无法进入右子树,即以该结点为根的子树被剪掉,但这也说明当前扩展结点的右儿子已被访问 
				}
			}
		}
		else
		{
			//当cnt[i] > 1时即cnt[i] == 2时,说明当前扩展结点的左右子树均已被访问则按照dfs规则向上回溯,返回上一层
			cnt[i] == 0;//将cnt[i]重新设置为0,使得下一次进入该层使为第一次进入.
			i--;		//回溯 
		}
	}
}

int main()
{
	int sum_p = 0, sum_w = 0;
	cin >> n >> c;
	for(int i = 0; i < n; i++)
	{
		cin>>objects[i].id;
		objects[i].flag = false; 
	}
	for(int i = 0; i < n; i++)
	{
		cin >> objects[i].w;
	}
	for(int i = 0; i < n; i++)
	{
		cin >> objects[i].p;
	}
	for(int i = 0; i < n; i++)
	{
		sum_p += objects[i].p;
		sum_w += objects[i].w;
		objects[i].pw = (1.0 * objects[i].p) / objects[i].w;
	}
	if(sum_w <= c)
	{
		cout << "背包能全装下" << endl;
		cout << "价值为:" << sum_p << endl;
		return 0;
	}
	sort(objects, objects + n, cmp);	//按物品单位重量价值非递减排序 
	Solution(0);
	cout << "装入背包的物品有:" << endl;						
	for(int i = 0; i < n; i++)
	{
		if(objects[i].flag == true)
			cout << objects[i].id << " "; 
	} 
	cout << endl; 
	cout << "物品装包最大价值为:" << endl; 
	cout << bestp << endl;
	cout << "装包价值最大时,物品重量为:" << endl;
	cout << bestw << endl;
	return 0;
}
 
/*
测试: 
7 150
A B C D E F G
35 30 60 50 40 10 25
10 40 30 50 35 40 30
*/

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值