问题:有一批共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!"); } } }