【装载问题】“回溯法”——《算法设计与分析(第五版)》


一、算法要求

有一批共n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且在这里插入图片描述要求确定是否有一个合理的装载方案可将集装箱装上这2艘轮船。如果有,找出一种装载方案。

1. 思路

解装载问题的回溯法中,算法MaxLoading返回不超过c的最大子集和,但并未给出达到这个最大子集和的相应子集。稍后加以完善。
算法MaxLoading调用递归函数Backtrack(1)实现回溯搜索。
Backtrack(i)搜索子集树中第i层子树。类Loading的数据成员记录子集树中结点信息,以减少传给Backtrack的参数。cw记录当前结点所相应的装载重量,bestw记录当前最大装载重量。
在算法 Backtrack中,当i>n时,算法搜索至叶结点,其相应的装载重量为cw。如果cw>bestw,则表示当前解优于当前最优解,此时应更新bestw。当i≤n时,当前扩展结点Z是子集树中的内部结点。该结点有x[i]=1和x[i]=0两个儿子结点。其左儿子结点表示x[i]=1的情形,仅当cw+w[i]≤c时进入左子树,对左子树递归搜索。其右儿子结点表示x[i]=0的情形。由于可行结点的右儿子结点总是可行的,因此进入右子树时不需检查可行性。
算法 Backtrack动态地生成问题的解空间树。在每个结点处算法花费O(1)时间。子集树中结点个数为O(2),故Backtrack所需的计算时间为O(2")。另外,Backtrack还需要额外的O(n)的递归栈空间。

2. 示例

在这里插入图片描述


二、完整代码

1. 主文件

main.cpp:

// Project1: 装载问题
#include<iostream>
#include<iomanip>
using namespace std;

template <class Type>
class Loading{
public:
	void Backtrack(int i);
	int n,			//集装箱数
		* x,		//当前解
		* bestx;	//当前最优解
	Type* w,		//集装箱重量数组
		c,			//第一艘轮船的载重量
		cw,			//当前载重量
		bestw,		//当前最优载重量
		r;			//剩余集装箱重量
};

template <class Type>
void  Loading <Type>::Backtrack(int i){// 搜索第i层结点
	if (i > n){// 到达叶结点
		if (cw > bestw){
			for (int j = 1; 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];
}

template<class Type>
Type MaxLoading(Type w[], Type c, int n, int bestx[])//返回最优载重量
{
	Loading<Type>X;
	//初始化X
	X.x = new int[n + 1];
	X.w = w;
	X.c = c;
	X.n = n;
	X.bestx = bestx;
	X.bestw = 0;
	X.cw = 0;
	X.r = 0;

	for (int i = 1; i <= n; i++){
		X.r += w[i];
	}

	X.Backtrack(1);
	delete[]X.x;
	return X.bestw;
}


int main(){
	int n = 3,
		m,
		c1 = 50,
		c2 = 50;
	int w[4] = { 0,10,40,40 },//对齐从1开始
		bestx[4];

	m = MaxLoading(w, c1, n, bestx);

	cout << "#The deadweight of the two ships is as follows: " << endl
		<< "C1: " << setw(3) << c1 << "\n" 
		<< "C2: " << setw(3) << c2 << endl;

	cout << "\n#Containers weigh as follows: " << endl;
	for (int i = 1; i <= n; i++){
		cout << "No." << i 
			<< ": " << w[i] << endl;
	}


	cout << "\n#The results of the backtracking algorithm are as follows: "
		<< endl << "#Include(index): ";
	for (int i = 1; i <= n; i++){
		if (bestx[i] == 1)
			cout << setw(3) << i;
	}
	cout << endl;

	return 0;
}

2. 效果展示

在这里插入图片描述


三、补充

回溯算法的优化:
如果当前节点的右子树不可能包含比当前最优解更好的解时,就不移动到右子树上;
设bestw为当前最优解,Z为解空间树的第i 层的一个节点。
限界函数: 在这里插入图片描述 为剩余货箱的重量;
cw+r<=bestw时,没有必要去搜索Z 的右子树:
当前载重量cw+剩余集装箱的重量r<=当前最优载重量bestw

文档供本人学习笔记使用,仅供参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NI'CE'XIAN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值