回溯法——(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加上下一个物品的重量wi不超过背包的容量c,就将该物品加入当前调度,并递归地搜索下一个节点。在搜索右子树时,不将当前物品加入调度,而是直接递归地搜索下一个节点。

这样,每次搜索左子树时,都会尝试将物品i加入调度,然后继续搜索下一个节点。如果在某个节点处,发现无法将物品i加入调度(即cw + wi > 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];
}

相关推荐
小向是个Der9 分钟前
嵌入式进阶——MCU上电后的动作
c语言·单片机·mcu·学习·链接脚本
silvamux_2 小时前
10款模型写种草笔记:可发布性、返工来源与成本数据缺口复盘
java·数据库·笔记
bkspiderx3 小时前
基于Socket的广播通信及C语言实现详解
c语言·开发语言·socket·广播·socket的广播通信
蒸蒸yyyyzwd3 小时前
kafka、redis、mysql不熟悉问题总结
笔记·八股
hgy89693 小时前
深度学习(小白笔记)——迁徙
人工智能·笔记·深度学习
摇滚侠3 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 11
java·笔记·python
辣知4 小时前
辣知·化智93 被历史尘封的鬻子
学习
每天题库6 小时前
西藏安全员 A 证备考真题题库 强化重点知识刷题提升通过率
学习·安全·考试·题库·考证
Be for thing6 小时前
PyTorch基础|Tensor张量详解,张量创建、数据类型与常用操作
pytorch·学习·1024程序员节
前进的李工6 小时前
Agent 学习笔记 02:Harness 与工作流怎么理解
笔记·学习·agent·工作流·harness