您的位置:首页 > 其它

【回溯法解决最优装载问题】

2016-10-31 08:17 288 查看
最近算法课在讲解回溯法,回溯法是一种利用树结构,遍历全部可能结果,并输出最优解的算法,它能够保证每一种结果都考虑到,不会出现漏掉的情况。

在用回溯法时,重要的一步操作是画出解空间树,之后按照深度优先便利的操作,搜索每一个节点,直到搜索至叶子结点。在搜索过程中,我们要考虑这个节点是否能够满足问题的限制,如果不满足,则尽早进行剪枝操作,以减少不必要的遍历。

具体问题描述如下: 有一批共n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且

,装载问题要求确定是否有一个合理的装载方案可将这些集装箱装上这2艘轮船。如果有,找出一种装载方案。

这个问题的基本策略非常清晰, (1)首先将第一艘轮船尽可能装满;

(2)将剩余的集装箱装上第二艘轮船。

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

具体代码如下:
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

/**
* Created by Amiedon on 2016/10/24.
*/
public class JiZhuangXiang {

private static JiZhuangXiang jzx = new JiZhuangXiang();
private static LNode root = jzx.new LNode(0,0,"");   //初始化一个根节点
private static double c1 = 40,c2 = 50;

public static void main(String[] args) {

double[] container = new double[]{4.1,5.2,6.3,7.4,8.5,9.6,10.7,11.8,12.9,3.2};
double sumWeight = 0;   //集装箱的总重量
for (double d : container)
sumWeight += d;
jzx.initialTree(container);  //初始化集装箱的解空间,即一棵二叉树
LNode lNode = jzx.backTrack();  //进行二叉树的遍历并找出尽量使C1装满的方案
System.out.println("船C1载重:"+lNode.weight+"   船C2载重:"+(sumWeight-lNode.weight));
System.out.println("船C1的装载方案为:"+lNode.trace+"(ps:其中数字表示第几个集装箱)");

}

/**
* 采用先序遍历二叉树,返回相应方案的叶子节点
* @return
*/
public LNode backTrack() {

Stack<LNode> stack = new Stack<LNode>();
LNode node = root;
double weight = 0;
LNode returnNode = root;
while (node != null || stack.size() > 0) {
while (node != null) {
if(node.lChild == null && weight < node.weight && node.weight < c1){
weight = node.weight;
returnNode = node;
}
stack.push(node);
node = node.lChild;
}
if (stack.size() > 0) {
node = stack.pop();
node = node.rChild;
}
}

return returnNode;
}

/**
* 初始化二叉树
* @param container
*/
public void initialTree(double[] container) {

Queue<LNode> queue = new LinkedList<LNode>();
queue.offer(root);
LNode temp;
LNode lChild;
LNode rChild;
int i = 1;
int deep;
while (!queue.isEmpty()){

deep = new Double(Math.log(i)/Math.log(2)).intValue();
if(deep >= container.length)
break;

temp = queue.poll();
lChild = new LNode(i,container[deep]+temp.weight,temp.trace+" "+(deep+1));
rChild = new LNode(i+1,temp.weight,temp.trace);
temp.setlChild(lChild);
temp.setrChild(rChild);
queue.offer(lChild);
queue.offer(rChild);

i++;
}

}

/**
* 定义了一个节点的内部类,用于存放左子树、右子树,以及节点的相关信息
*/
public class LNode{

int num;     //节点编号
double weight;   //到该节点为止,船C1所装载的重量
LNode lChild;
LNode rChild;
String trace;    //用于跟踪船C1集装箱的装载情况

LNode(int num,double weight,String trace){
this.num = num;
this.weight = weight;
this.trace = trace;
}

public void setlChild(LNode lChild) {
this.lChild = lChild;
}

public void setrChild(LNode rChild) {
this.rChild = rChild;
}
}
}
在此,我们采用队列实现对树的遍历,在便利过程中检查每一个节点,进行剪枝操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法