以回溯的思想求解0-1背包问题

2023-05-16

以回溯法的思想求解0-1背包问题

目录

介绍

求解


介绍

  • 0-1背包问题

[问题描述]给定n种物品和一背包。物品i的重量是wi,其价值为pi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?

  • 回溯法解题的基本思想
  1. 按贪心法的思路,优先装入单位价值高的物品。
  2. 当剩余容量装不下最后考虑的物品时,再用回溯法修改先前的装入方案,直到得到全局最优解为止。

求解

#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cstring>
using namespace std;
struct object
{
	int p;	//物品的价值 
	int w;	//物品的重量 
}*ob;

int cp = 0 , bestp = 0;	//cp存放当前的价值,bestp存放最优价值 
int num;	//物品的数量
int c;		//背包的容量 
int* x, *bestx; 

//自定义大小关系 
bool cmp(object a, object b)
{
	return a.p/a.w > b.p/b.w;
}

//输出
void putout()
{
	cout<<"物品放入背包的状态为:";
	for(int i = 0; i < num; i++)
	{
		cout<<setw(5)<<x[i];
	}
	
	cout<<" 获得的价值为:"<<cp<<endl;
}

//回溯法求0-1背包
void knap(int t)	//t表示递归深度 
{
	if(t >= num)	//到达叶子节点 
	{
		if(bestp < cp)
		{
			bestp = cp;
			for(int i = 0; i < num; i++)
			{
				bestx[i] = x[i];
			}
		}
		
		putout();
	}
	
	else
	{
		if(ob[t].w <= c)	//搜索左枝,约束条件 
		{
			x[t] = 1; cp += ob[t].p; c -= ob[t].w;	//改变状态 
			knap(t+1);	//继续向下深度搜索
			x[t] = 0; cp -= ob[t].p; c += ob[t].w;	//恢复原有状态 
		}
		x[t] = 0;		//搜索右枝,无需约束条件 
		knap(t+1);
	}
} 

int main()
{
	cout<<"请输入物品的数量:";
	cin>>num;
	
	ob = new object[num];
	cout<<"请输入每个物品的重量:";
	
	for(int i = 0; i < num; i++)
	{
		cin>>ob[i].w;
	}
	
	cout<<"请输入每个物品的价值:";
	for(int i = 0; i < num; i++)
	{
		cin>>ob[i].p;
	}
	
	cout<<"请输入背包的容量:";
	cin>>c;
	
	x = new int[num];	//存放当前解
	bestx = new int[num];	//存放最优解
	
	sort(ob,ob+num,cmp);	//将物品根据单位价值从高到低排列 
	
	knap(0);
	
	cout<<"最优价值:"<<bestp<<"\t";
	for(int i = 0; i < num; i++)
	{
		if(bestx[i] == 1)
		{
			cout<<i+1<<" ";
		}
	}
	
	return 0;
}

据此形成的部分空间树,如下图所示,其中的w[i]与代码中的ob[i].w相对应

运行结果截图:


前面算法完全搜索解空间树策略: 用约束条件确定是否搜索其左子树; 对右子树没有任何判断,一定进入右子树搜索,十分耗时,下面在原有代码基础上加入剪枝函数(右子树有可能包含最优解时才进入右子树搜索,否则将右子树减去)以优化。

剪枝方法具体如下:

  1. r:当前剩余物品价值总和;
  2. cv:当前获得价值;
  3. bestp:当前最优价值;
  4. 当cv+r<=bestp时,可剪去右子树。

计算右子树上界方法:

将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。由此得到的价值是右子树中解的上界。

代码如下:

#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cstring>
using namespace std;
struct object
{
	int p;	//物品的价值 
	int w;	//物品的重量 
}*ob;

int cp = 0 , bestp = 0;	//cp存放当前的价值,bestp存放最优价值 
int num;	//物品的数量
int c;		//背包的容量 
int cw = 0;		//计算当前的重量 
int* x, *bestx; 

//自定义大小关系 
bool cmp(object a, object b)
{
	return a.p/a.w > b.p/b.w;
}

//输出
void putout()
{
	cout<<"物品放入背包的状态为:";
	for(int i = 0; i < num; i++)
	{
		cout<<setw(5)<<x[i];
	}
	
	cout<<" 获得的价值为:"<<cp<<endl;
}

//计算以结点i处为根节点的上界(限界条件) 
int bound(int i)
{
	int left = c - cw;	//剩余容量 
	int temp = cp;	 //当前价值
	while(ob[i].w <= left && i <= num)
	{
		temp += ob[i].p;
		left -= ob[i].w;
		i++;	
	} 
	
	//装满背包
	if(i <= num)
	{
		temp += left*ob[i].p/ob[i].w;	
	} 
	
	return temp;
}

//回溯法求0-1背包
void knap(int t)	//t表示递归深度 
{
	if(t >= num)	//到达叶子节点 
	{
		if(bestp < cp)
		{
			bestp = cp;
			for(int i = 0; i < num; i++)
			{
				bestx[i] = x[i];
			}
		}
		
		putout();
	}
	
	else
	{
		if(ob[t].w <= c)	//搜索左枝,约束条件 
		{
			x[t] = 1; cp += ob[t].p; c -= ob[t].w;	//改变状态 
			knap(t+1);	//继续向下深度搜索
			x[t] = 0; cp -= ob[t].p; c += ob[t].w;	//恢复原有状态 
		}
		
		if(bound(t+1) > bestp)	//限界搜索右枝 
		{
			x[t] = 0;		
			knap(t+1);
		}		
	}
} 

int main()
{
	cout<<"请输入物品的数量:";
	cin>>num;
	
	ob = new object[num];
	cout<<"请输入每个物品的重量:";
	
	for(int i = 0; i < num; i++)
	{
		cin>>ob[i].w;
	}
	
	cout<<"请输入每个物品的价值:";
	for(int i = 0; i < num; i++)
	{
		cin>>ob[i].p;
	}
	
	cout<<"请输入背包的容量:";
	cin>>c;
	
	x = new int[num];	//存放当前解
	bestx = new int[num];	//存放最优解
	
	sort(ob,ob+num,cmp);	//将物品根据单位价值从高到低排列 
	
	knap(0);
	
	cout<<"最优价值:"<<bestp<<"\t";
	for(int i = 0; i < num; i++)
	{
		if(bestx[i] == 1)
		{
			cout<<i+1<<" ";
		}
	}
	
	return 0;
}

由此形成的空间树为

运行结果:

至此问题得到了一种解答。

注:这是本人第一次写博客,如有不足之处敬请批评指教!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

以回溯的思想求解0-1背包问题 的相关文章

  • android 五大布局(转)

    大家好 我们这一节讲一下Android对用五大布局对象 它们分别是FrameLayout 框架布局 不知道是不是这么翻译的 LinearLayout 线性布局 AbsoluteLayout 绝对布局 RelativeLayout 相对布局
  • flex校验

    package cn newtouch flexDemo business validator import mx binding utils BindingUtils import mx containers FormItem impor
  • docker--最全的网络类型(7类)

    目录 1bridge桥接模式 xff08 默认 xff09 2 host 3 overlay 4 ipvlan 5 macvlan 6 none 7 container 官网有六类 xff1a Networking overview Doc
  • flex 国际化

    Flex 开发 xff08 国际化 xff09 在Flex4 之前默认只支持en US ja JP 这两种本地化 xff0c 因此如果想在Flex 中支持中文或者其他语言时 xff0c 需要额外的操作 xff1a 1 首先添加新的本地化支持
  • 使用mysqladmin命令来修改mysql的root密码

    一般mysql的root默认密码为空 xff0c 如果你之前并没有设置过root密码就使用mysqladmin命令 xff0c 你可以使用如下mysqladmin命令来修改root密码 1 2 3 mysqladmin u root p p
  • CCF化学方程式的配平

    include lt iostream gt include lt string gt include lt cctype gt include lt unordered map gt using namespace std unorder
  • CCF 201909-4 推荐系统

    include lt cstdio gt include lt set gt include lt unordered map gt include lt algorithm gt using namespace std typedef l
  • CCF 201903-4 消息传递接口

  • Docker学习笔记(一)

    Docker学习笔记 xff08 一 xff09 什么是Docker Docker 使用 Google 公司推出的 Go 语言 进行开发实现 xff0c 基于 Linux 内核的 cgroup xff0c namespace xff0c 以

随机推荐

  • Docker学习笔记(二)

    Docker镜像 Docker 镜像是一个特殊的文件系统 xff0c 除了提供容器运行时所需的程序 库 资源 配置等文件外 xff0c 还包含了一些为运行时准备的一些配置参数 xff08 如匿名卷 环境变量 用户等 xff09 Docker
  • Java多线程里共享变量线程安全问题的原因

    Java多线程里共享变量线程安全问题的原因 Java多线程里对于共享变量的操作往往需要考虑进行一定的同步互斥操作 xff0c 原来是因为Java内存模型导致的共享内存对于线程不可见 Java 内存模型规定 xff0c 将所有的变量都存放在主
  • 重构-改善既有代码的设计读书笔记一

    重构 定义 为何重构 改进软件设计 使软件更容易理解 帮助找到Bug提高编程速度 何时重构 添加功能修改错误复审 总而言之 xff0c 当你觉得代码的可读性 可维护性 可修改性到达一定难以接受的程度 xff0c 就可以开始考虑是否可以使用重
  • Spring文档学习笔记一

    Spring文档学习笔记一 目录 Spring文档学习笔记一 Spring的宗旨 主要特征 几个核心理念 IoC 依赖解析过程 Spring循环依赖的解决方式 更详细的得估计得看Spring源码 1 4 2 Dependencies and
  • python数据结构算法DAY2| 快速排序

    目录 快速排序 xff08 quick sort xff09 1 什么是快速排序 2 快速排序思路 3 快速排序代码 4快速排序复杂度 5 快速排序函数与冒泡排序的效率比较 6 快速排序的缺点 解决办法 xff1a 快速排序 xff08 q
  • Go里w http.ResponseWriter,调用w.Write()方法报错

    Go里w http ResponseWriter写入报错http request method or response status code does not allow 1 下面是报错截图 2 点进去Write方法 它首先是一个接口 x
  • CCF 202012-3 带配额的文件系统 练习

    大模拟 xff0c 没涉及什么算法主要是数据结构的设计 细节的考虑 xff0c 挺锻炼的 xff0c 记录一下 xff0c 代码加了注释 include lt iostream gt include lt string gt include
  • 多接口继承和多层抽象类设计理解

    多接口继承和多层抽象类设计理解 以JDK集合List框架为例有感 以后可能又会有新的理解 xff0c 先记录一下 设计得好的接口一般也要遵循单一职责原则 xff0c 最上层的接口一般属于独立的 xff0c 不再有依赖的 xff0c 如Ite
  • 202012-5 星际旅行 (线段树模板60分)记录一下

    include lt bits stdc 43 43 h gt using namespace std typedef long long ll const int maxn 61 1e5 43 5 const ll MOD 61 1e9
  • 联机象棋(1)

    联机象棋 xff08 1 xff09 需求架构与开发技术主要设计与实现1 棋盘 棋子布局2 选棋 下棋3 人人对战匹配4 判断是否被将5 通信模块6 其他如声音效果等提升用户体验7 人机对战 尚未实现 8 最终实现效果图 需求 登录 注册
  • AbstractQueuedSynchronizer源码阅读(1)(AQS JDK1.8)

    AbstractQueuedSynchronizer 前言AbstractQueuedSynchronizer xff08 1 xff09 JDK 1 8 用途主要源码分析Node内部类ConditionObject类重要方法 主要的属性及
  • ReentrantLock源码阅读(1)(JDK1.8)

    ReentrantLock 前言ReentrantLock JDK 1 8 实现了Lock接口Sync类NonfairSync类FairSync类重要属性和方法 总结 前言 最近在使用Java 并发包时遇到一些问题 xff0c 感觉对于其还
  • SpringBoot整合Kafka控制消费启停遇到的问题记录(@KafkaListener注解使用)

    最近在做一个SpringBoot整合Kafka的一个项目 xff0c 需要控制Kafka客户端消费数据的停止与启动 xff0c 遇到一个问题 xff0c 排查下来感觉对自己有一定帮助 xff0c 趁此记录一下 配置KafkaListener
  • 我的第一次实质性开源贡献——Apache IoTDB

    前言 虽然之前也在Github上尝试提过一些PR 但都是一些doc typo等类型的入门实践 真正算得上有一定实质性工作 xff0c 要数最近在Apache IoTDB上提交的一个功能PR 如果大家对开源感兴趣的话 xff0c 可以看我的一
  • 开源相关知识介绍

    以下是自己网上搜集的一些有关开源的一些背景知识进行分享 xff0c 欢迎对开源感兴趣的同学可以阅读 xff0c 跟我一起走进开源 拥抱开源 目录 一 开源项目的演进 二 开源项目的成功案例 Apache Linux Mozilla Ubun
  • python数据结构算法DAY3| 堆排序

    目录 前言 1 什么是堆排序 xff1f 堆的向下调整性质 2 堆排序思路 3 堆排序代码 python中堆排序的内置模块 4 堆排序时间复杂度 5 堆排序解决topk问题 前言 堆排序是基于完全二叉树 xff0c 堆是一种特殊的完全二叉树
  • Apache IoTDB’s UDF源码分析(1)

    目录 前言 命令行注册UDF函数 Create Function xxx as 34 全限定类名 34 语法分析 生成物理计划 执行物理计划进行函数注册 Select带有UDF函数的查询 前言 继上个月开始了Apache IoTDB的源码贡
  • 新手入门贡献Apache IoTDB

    名词解释 Issue 开源社区的一个任务的统称 xff0c 通常会有一个Issue 列表 xff0c 用于表示各种任务 xff0c 比如功能Issue Bug Issue Improvement Issue等 PR Pull Request
  • Apache IoTDB介绍

    什么是时序数据库 时序数据库 为万物互联插上一双翅膀 有态度的HBase Spark BigData 总体介绍 Apache IoTDB 始于清华大学软件学院 xff0c 是一款时序数据库 主要使用场景是在物联网相关行业 xff0c 如 x
  • 以回溯的思想求解0-1背包问题

    以回溯法的思想求解0 1背包问题 目录 介绍 求解 介绍 0 1背包问题 问题描述 给定n种物品和一背包 物品i的重量是wi xff0c 其价值为pi xff0c 背包的容量为C 问应如何选择装入背包的物品 xff0c 使得装入背包中物品的