用回溯算法解装载问题,Java实现

来源:互联网 发布:淘宝分销商赚钱吗 编辑:程序博客网 时间:2024/05/18 20:10

问题描述:
有一批共有 n 个集装箱要装上两艘载重量分别为 c1 和 c2 的轮船,其中集装箱 i 的重量为 w[i], 且重量之和小于(c1 + c2)。装载问题要求确定是否存在一个合理的装载方案可将这 n 个集装箱装上这两艘轮船。如果有,找出一种装载方案。
例如,当n=3,c1=c2=50,且w=[10,40,40]时,可将集装箱1和集装箱2装上一艘轮船,而将集装箱3装在第二艘轮船;如果w=[20,40,40],则无法将这3个集装箱都装上轮船。
容易证明,如果一个给定的装载问题有解,则采用如下的策略可以得到最优装载方案。
1.首先将第一艘轮船尽可能装满。
2.将剩余的集装箱装上第二艘轮船。
将第一艘轮船尽可能的装满等价于选取全体集装箱的子集,使该子集中集装箱的重量之和最接近 c1 。因此,等价于一个特殊的 0-1 背包问题。 因此是一棵子集树。
max(w1x1+w2x2+…+wixi)
(w1x1+w2x2+…+wixi)<= c1;
xi @{0,1},1<=i<=n
当然可以用第三章中讨论过的动态规划算反解这个特殊的0-1背包问题。所需的计算时间是O(min{c1,2n})。下面讨论用回溯法设计解装载问题O(2n)计算时间算法。在某些情况下该算法优于动态规划算法。
注:该问题只能应用于有且只有两艘轮船的情况。

package n20_装载问题回溯算法;/* * 用回溯算法解装载问题 */public class Main {    static int[]w={ 0, 5, 30, 10, 1, 20, 40, 20}; // 4个集装箱,w[0]放弃    static int n = w.length - 1;//集装箱的个数    static int bestw = 0;//当前的最优值    static int cw = 0;//当前轮船的装载量    static int c =58;//轮船最大的核载量    static int r = 0;//剩余集装箱的重量    static int []bestx=new int [n+1];//用来存放当前的最优解    static int x[]=new int[n+1];//当前解    public static void main(String[] args) {        for (int i = 1; i <= n; i++) {//刚开始时,剩余的集装箱重量是所有集装箱的重量            r += w[i];        }        int tempR=r;//临时变量,求最优解时要用到        f(1);//从第一个集装箱k        System.out.println(bestw);//输出最优值        //以下各步骤是求最优解的        cw = 0;//重置当前轮船的装载量        r = tempR;//重置剩余集装箱的重量        f2(1);  //f2是用来求最优解的,和f稍有不同        for(int i=1;i<=n;i++){  //输出最优解            System.out.print(bestx[i]+" ");        }    }    private static void f2(int i) {        //搜索第i层结点        if (i > n) { //到达叶结点            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];            f2(i + 1);            cw -= w[i];        }        if (cw + r >= bestw) { //搜索右子树(特别注意,这里是>=号,而在f()方法的这里可以不需要).            x[i]=0;            f2(i + 1);        }        r += w[i];    }    private static void f(int i) {        //搜索第i层结点        if (i > n) { //到达叶结点            bestw = cw;            return;        }        //搜索子树        r -= w[i];        if (cw + w[i] <= c) { //搜索左子树            cw += w[i];            f(i + 1);            cw -= w[i];        }        if (cw + r > bestw) { //搜索右子树            f(i + 1);        }        r += w[i];    }}
1 0