好理解的基于二叉树的0-1背包求解问题

问题描述:

给定n(n<=100)种物品和一个背包。物品i的重量是wi,价值为vi,背包的容量为C(C<=1000)。问:应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品i装入多次,也不能只装入部分物品i。

输入样例1:

在这里给出一组输入。例如:
5 10
2 6
2 3
6 5
5 4
4 6

输出样例1:

15

输入样例2:

6 60
15 32
17 37
20 46
12 26
9 21
14 30

输出样例2:

134

———————————————————————

建立一个二叉树,我们约定左结点表示选择装入物品,右节点表示不装入物品。

假设有5个物品,
物品重量是{2, 2, 6, 5, 4}
对应价值是{6, 3, 5, 4, 6}
约定所有物品装入背包后,背包的总重量不超过10

============================
对于第一个物品,我们可以选择装入或者不装入
若装入,总重量weigth = 2, 总价值value = 6
若不装入,总重量weigth = 0, 总价值value = 0

============================
对于第二个物品,我们可以选择装入或者不装入
若第一个物品已经装入,则
若装入第二个物品,总重量weigth = 4, 总价值value = 9
若不装入第二个物品,总重量weigth = 2, 总价值value = 6

若第一个物品没有装入,则
若装入第二个物品,总重量weigth = 2, 总价值value = 3
若不装入第二个物品,总重量weigth = 0, 总价值value = 0

============================
… … … … …

依次类推,我们可以构造出这样的二叉树(局部)
在这里插入图片描述

那么可以知道背包里的物品最大值在二叉树的叶子结点那里。

#include<iostream>
#include<cstdlib>
using namespace std;
//不要向结构体直接定义默认值,可以通过一个函数进行基本的初始化
typedef struct TreeNode{
	long long int val;
	long long int weight;
	bool left;//是否已经访问左子树
	bool right;//是否已经访问右子树
	TreeNode *LChild;//左节点
	TreeNode *RChild;//右节点
	TreeNode *Parent; //父结点
}TreeNode;

TreeNode *init(TreeNode *node){
	node->weight = 0;//记录当前结点已经装的重量和价值(边装边计算)
	node->val = 0;
	node->left = false;//是否已经访问左子树
	node->right = false;//是否已经访问右子树
	node->LChild = NULL;//左节点
	node->RChild = NULL;//右节点
	node->Parent = NULL; //父结点
	return node;
}

int n, c;//个数,规定总重量
long long int MAX=0;
TreeNode *addNode(TreeNode *parented, int w, int val){
	TreeNode *node=(TreeNode *)malloc(sizeof(TreeNode));
	node = init(node);
	// node.data = data;//
	if(parented->weight+w <= c){//可以装
		node->weight = w + parented->weight; //记录已有重量
		node->val = val + parented->val;
		node->Parent = parented;
	}else{
		
		node=NULL;//装不下,返回空
	}
	
	return node;
}


int main(int argc, char const *argv[])
{
	TreeNode *head=(TreeNode *)malloc(sizeof(TreeNode));
	
	//w记录重量,p记录价值,(i+1)表示第几个物品(i=0是第一个物品,w[0], p[0]表示第一个物品的重量和价值) (0<=i<n)
	int w[1001], p[1001],i;
	cin>>n>>c;
	//初始化数组
	for(i=0; i<n; i++){
		cin>>w[i]>>p[i];
	}

	head = init(head);

	i=0;
	while(1){
		if(head->left&&head->right&&head->Parent==NULL){//头结点已经将左右子树访问完,退出循环
			cout<<"结束"<<endl;
			break;
		}
		if(head->left&&head->right){//子节点都访问了,就回退
			cout<<"回退"<<endl;
			i--;//返回到上一个数据继续尝试
			head = head->Parent;
			continue;
		}
		if(i>=n||head->weight>=c){//加完数据了或者背包已经装满了
			cout<<"加完数据了或者已经装满了"<<endl;
			if(MAX < head->val)MAX = head->val;//保存最大值

			head->left = true;
			head->right = true;
			continue;
		}

		if(!head->left){//左边没有插入过
			cout<<"加左结点 i = "<<i<<endl;
			head->left = true;//表名我已经访问当前结点的左结点
			head->LChild = addNode(head, w[i], p[i]);//表示将当前结点插入
			if(head->LChild!=NULL){//左边插入成功
				i++;//下一个数据
				head = head->LChild;
			}
		}else if(!head->right){//右边没有插入过
			cout<<"加右结点  i = "<<i<<endl;
			head->right = true;//表名我已经访问当前结点的右结点
			
			head->RChild = addNode(head, 0, 0);//表示跳过当前物品的装入,去下一个物品
			if(head->RChild!=NULL){//右边边插入成功
				i++;
				head = head->RChild;
			}
		}
	}
	//通过先序遍历,即可获得背包中物品的最大总价值。也可给出装入物品的序列
	cout<<MAX<<endl;

	return 0;
}

在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值