问题:有一批共n个集装箱要装上2艘载重量分别为c1,c2的轮船,其中集装箱i的重量为wi,且要求确定是否有一个合理的装载方案可将这n个集装箱装上这2艘轮船。
抽象:将n个物品放到2个容器中,每个容器不能超重,寻找一个可行的方案。
思路:先尽可能的将第一艘船装满,然后将剩余的集装箱装到第二艘船上,如果第二艘船装不下则问题无解。则问题转化为求第一艘船最大装载量。使用分支定界法进行解空间搜索。
代码如下:
package test;
import java.util.LinkedList;
/**
* 用分支定界法求装载问题
*
* @author yanghang
*
*/
public class Fenzhidingjie {
private static float[] w = { 40, 40, 10 }; // 货箱质量数组
private static int n = w.length; // 货箱数量
private static float c1 = 50; // 第一艘船的载重量
private static float c2 = 50; // 第二艘船的载重量
private static float bestw; // 第一艘船的最大装载
private static float ew; // 当前船的装载量
private static LinkedList<Float> mq = new LinkedList<Float>(); // FIFO队列
/**
* 最优装载值
*
* @param c
*/
public static float maxLoading(float c) {
mq.addLast(new Float(-1)); // 初始化结点队列,标记分层
int i = 0; // E-结点的层
ew = 0; // 当前船的装载量
bestw = 0; // 目前的最优值
while (!mq.isEmpty()) { // 搜索子集空间树
if (ew + w[i] <= c) { // 检查E-结点的左孩子,货箱i是否可以装载
addLiveNode(ew + w[i], i); // 货箱i可以装载
}
addLiveNode(ew, i); // 右孩子总是可行的,不装载货物i
ew = (Float) mq.removeFirst(); // 取下一个结点
if (ew == -1) { // 到达层的尾部
if (mq.isEmpty()) {
return bestw;
}
mq.addLast(new Float(-1));
ew = (Float) mq.removeFirst(); // 取下一个结点
i++; // ew的层
}
}
return bestw;
}
/**
* 入队
*
* @param wt
* @param i
*/
public static void addLiveNode(float wt, int i) {
if (i == n - 1) { // 下标从0开始,是叶子
if (wt > bestw) {
bestw = wt;
}
} else { // 不是叶子
mq.addLast(new Float(wt));
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
// 所有货箱的重量之和
float s = 0;
for (float f : w) {
s += f;
}
if (s <= c1 || s <= c2) {
System.out.println("need only one ship!");
}
if (s > c1 + c2) {
System.out.println("no solution!");
return;
}
float bestw = Fenzhidingjie.maxLoading(c1);
if (s - bestw <= c2) {
System.out.println("The first ship loading " + bestw);
System.out.println("The second ship loading " + (s - bestw));
} else {
System.out.println("no solution!");
}
}
}