回溯法——(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];
}

相关推荐
喜欢吃燃面43 分钟前
C++算法竞赛:位运算
开发语言·c++·学习·算法
传奇开心果编程44 分钟前
【传奇开心果系列】Flet框架实现的家庭记账本示例自定义模板
python·学习·ui·前端框架·自动化
草莓熊Lotso1 小时前
《详解 C++ Date 类的设计与实现:从运算符重载到功能测试》
开发语言·c++·经验分享·笔记·其他
_Kayo_7 小时前
node.js 学习笔记3 HTTP
笔记·学习
CCCC131016310 小时前
嵌入式学习(day 28)线程
jvm·学习
星星火柴93611 小时前
关于“双指针法“的总结
数据结构·c++·笔记·学习·算法
小狗爱吃黄桃罐头11 小时前
正点原子【第四期】Linux之驱动开发篇学习笔记-1.1 Linux驱动开发与裸机开发的区别
linux·驱动开发·学习
艾莉丝努力练剑12 小时前
【洛谷刷题】用C语言和C++做一些入门题,练习洛谷IDE模式:分支机构(一)
c语言·开发语言·数据结构·c++·学习·算法
武昌库里写JAVA13 小时前
JAVA面试汇总(四)JVM(一)
java·vue.js·spring boot·sql·学习
Cx330❀13 小时前
【数据结构初阶】--排序(五):计数排序,排序算法复杂度对比和稳定性分析
c语言·数据结构·经验分享·笔记·算法·排序算法