回溯法:最大装载问题(使用递归,优化无最优解时候的右子树搜索)

// 16x2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


// 优化了回溯代码
// 即不移动到不可能包含比当前最优解还要好的解的右子树

#include<iostream.h>

template<class T>
class Loading {
friend GetMaxLoading(T [], T, int);
private:
void Compute(int i);
int n; // 货箱数量
T *w, // 货箱重量数组
c, // 第一艘船的容量
cw, // 当前的装载重量
bestw, // 目前最优装载重量
r; // 剩余货物的重量之和
};

// 树的每个节点上,都有唯一的剩余货物重量r
template<class T>
void Loading<T>::Compute(int i)
{
if (i > n) {
// 与16x1有所不同,只要到移到了叶节点,就必定是最佳方案
// 暂时的理解:只有cw + w[i] <= c时,会继续往下深入,当前x就一定取值了。
// 或者cw + r > bestw时,也会继续往下深入,但这样一定会最优解吗?
bestw = cw;
return;
}

// r是全局变量,减去当前货物的重量
// 无论放得下还是放不下,先减去当前货物重量再说
cout << "r-w[" << i <<"]=" << r << "-" << w[i] << "=";
r -= w[i];
cout << r<< endl;

// 装下当前货物的时候,情况与16x1一模一样
if (cw + w[i] <= c) { // try x[i] = 1
cw += w[i];
Compute(i+1);
cw -= w[i];
}
// 如果当前装载货物的之和,加上剩余货物重量之和,是否大于当前最优装载
// 16x1里每次必定继续往下计算,但现在不一定了
// 真正的意义:当前节点一定要取x=1的情况,即左孩子的值;否则一定得不到最优解
// 不取当前值的话,即使所有剩余货物r+当前装载cw,也不能比当前bestw更好
// 这个判断在每个节点都有,只要比较当前节点的bestw就行了
cout << " cw="<< cw <<", r="<< r << "" << ", bestw=" << bestw << endl;
if (cw + r > bestw) // try x[i] = 0
Compute(i+1);
else
cout << " cw + r > bestw,不必计算了!" << endl;
// r恢复当前货物重量的值,退回准备从更高层去遍历
r += w[i];
}

template<class T>
T GetMaxLoading(T w[], T c, int n)
{
Loading<T> X;
X.w = w;
X.c = c;
X.n = n;
X.bestw = 0;
X.cw = 0;

// r的初始值为所有重量之和
X.r = 0;
for (int i = 1; i <= n; i++)
X.r += w[i];

// 计算最佳装载,从1层算起(很重要)。0层是无用值,放弃。
X.Compute(1);
// 因为是自动递归计算,所以返回的时候,已经得到了最大装载值
// 返回的是全局变量的最大装载值,不是Compute函数的返回值
return X.bestw;
}

void main(void)
{
/* int w[6] = {0, 5, 1, 6, 2, 7};
int n = 5;
int c = 10;
*/
int w[5] = {0, 8, 6, 2, 3};
int n = 4;
int c = 12;

cout << "Value of max loading is" << endl;
cout << GetMaxLoading(w,c,n) << endl;
}
posted @ 2012-02-10 03:43  findumars  Views(1762)  Comments(0Edit  收藏  举报