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

问题描述:
有一批共有 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];
    }
}
发布了29 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/shuoyuechenxing/article/details/70550671
今日推荐