回溯法——(1)装载问题(C语言讲解)

目录

一、装载问题

1.问题概括:

2.解决方案(思路):

3.图片讲解(超详细):

4.代码分析:

二、算法改进:引入上界函数

1.问题概念:

2.图片讲解:

3.升级代码分析:


一、装载问题

1.问题概括:

有一批共n个集装箱要装上2艘载重量分别为c1和c1的轮船,其中集装箱i重量为wi,且

装载问题要求确定是否有一个合理的装载方案可将这一批集装箱装上这2艘轮船。

如果有,找出一种装载方案。

2.解决方案(思路):

  1. 首先将第一艘轮船尽可能装满。
  2. 将剩余的集装箱装上第二艘轮船。

将第一艘船尽可能装满,等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近c1。因此,装载问题等价于特殊的0-1背包问题

由此可知,需要满足一下条件:

其中,

3.图片讲解(超详细):

假设一艘船能装载货物的重量为9,货物有三个,重量w(weight)分别为6,3,1。并且要求第一艘船重量最大(max),不能超重。

讲解:

设定第一艘为c1,

第一次在c1船上放置重量为6的货物,

此时c1已装入6,剩余3,

第二次在继续向c1船上放置重量为3的货物,

此时c1已装入6+3=9,剩余0,

第三次如过在继续向c1船上放置重量为1的货物,

此时c1船已装入6+3+1=10≥9,剩余-1,

说明c1船超载了,也就是说,第三次重量为1的货物应该装载在第二艘船c2,第一艘不装。

由此,我们将放入第一艘船的标为1,不放入第一艘船的标为0,构建二叉树,得到我们第一串存放方式的编码,110

以此类推,得出其他可能,但我们需在代码中设置bestw用于存储最优解,也就是哪一个线路或者说是方案策略,可以使得该线路或者该方案策略得出的c1接近最大值max。

其实该方法可以总结为:

++从一条路往前走,能进则进,不能进则退回来,换一条路再试。++

这种走不通就退回再走的技术为回溯法。

4.代码分析:

cpp 复制代码
/*
    cw:当前重量
    bestw:最佳重量
    x[]:当前调度
    bestx[]:最有调度
    c:容量
    w[]:货物重量
*/

//搜索第i层节点
void backtrack(int i) {
	//到达叶子节点
	if (i > n) {
		if (cw > bestw)
			return;
	}
	//搜索左子树
	if (cw + w[i] <= c) {
		cw += w[i];
		backtrack(i + 1);
        
		cw -= w[i];
	}
    //不行就去下一级继续
    backtrack(i+1);
}

在函数backtrack中,当到达叶子节点(即i > n)时,会检查当前的总重量cw是否大于已知的最佳重量bestw。如果是,就返回;否则,继续搜索。

在搜索左子树时,如果当前的总重量cw加上下一个物品的重量w[i]不超过背包的容量c,就将该物品加入当前调度,并递归地搜索下一个节点。在搜索右子树时,不将当前物品加入调度,而是直接递归地搜索下一个节点。

这样,每次搜索左子树时,都会尝试将物品i加入调度,然后继续搜索下一个节点。如果在某个节点处,发现无法将物品i加入调度(即cw + w[i] > c),就会转而搜索右子树,即不将物品i加入调度,而是直接递归地搜索下一个节点。这种搜索方式保证了所有可能的调度都被尝试过了,从而可以找到最优解。


二、算法改进:引入上界函数

1.问题概念:

设r是剩余集装箱的重量,即

定义上界函数为cw+r。若cw+r≤bestw,即当前重量+剩余集装箱重量比最佳重量还小,则可将当前结点的右子树减去,即不装的情况不用考虑。从而节省内存消耗以及代码计算时间。

还是上面那个例子:

假设一艘船能装载货物的重量为9,货物有三个,重量w(weight)分别为6,3,1。并且要求第一艘船重量最大(max),不能超重。

2.图片讲解:

3.升级代码:

cpp 复制代码
/*
    cw:当前重量
    bestw:最佳重量
    x[]:当前调度
    bestx[]:最有调度
    c:容量
    w[]:货物重量
*/
//搜索第i层节点
void Backtrack(int i) {
	//如果到达叶子结点
	if (i > n) {
		if (cw > bestw) {
			for (int j = i; j <= n; j++) {
				bestx[j] = x[j];//更新最优解
				bestw = cw;
			}
		}
		return;
	}
	r -= w[i];
	//搜索左子树
	if (cw + w[i] <= c) {
		x[i] = 1;
		cw += w[i];
		Backtrack(i + 1);
		cw -= w[i];
	}
	if (cw + r > bestw) {
		x[i] = 0;//搜索右子树
		Backtrack(i + 1);
	}
	r += w[i];
}

相关推荐
嵌入式科普6 分钟前
十三、从0开始卷出一个新项目之瑞萨RZN2L串口DMA接收不定长
c语言·stm32·瑞萨·e2studio·rzn2l
biter008810 分钟前
opencv(15) OpenCV背景减除器(Background Subtractors)学习
人工智能·opencv·学习
车轮滚滚__21 分钟前
uniapp对接unipush 1.0 ios/android
笔记
Code哈哈笑1 小时前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
小林熬夜学编程2 小时前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
Jackey_Song_Odd2 小时前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
QQ同步助手2 小时前
如何正确使用人工智能:开启智慧学习与创新之旅
人工智能·学习·百度
流浪的小新2 小时前
【AI】人工智能、LLM学习资源汇总
人工智能·学习
A懿轩A3 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
云边有个稻草人3 小时前
【优选算法】—复写零(双指针算法)
笔记·算法·双指针算法