C++回溯法装载问题

题目描述

在这里插入图片描述

题目分析

这个问题其实是一个NP难问题,当w1+w2+…wn=c1+c2的时候,装载问题等价于子集和问题。当c1=c2的时候,等价于划分问题.这两个问题都是NP难的问题,所以我们装载问题也是NP难的问题。关于这篇文章的先前相关知识请见回溯法框架

如果一个给定的装载问题有解,则我们采用的策略应该是:先将第一艘轮船尽可能装满,然后将剩余的集装箱上第二艘轮船。将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近c1。由此可知,装载问题等价于特殊的0-1背包问题。

算法设计

这个题目我们可以用我们上次说过的子集树来解决,在某一层,如果此时的装载容量大于了c1,我们就应该将这个子树进行剪枝。我们使用Backtrack(i)搜索子集树中第i层子树。类Loading的数据成员记录子集树中结点信息,以减少传给Backtrack的参数。cw记录当前结点所相应的装载重量,bestw记录当前最大装载重量。
在Backtrack中,当i>n时,算法搜索至叶节点,其相应的装载重量为cw。如果cw>bestw,则表示当前解优于当前最优解,此时应该更新bestw。
当i<=n时,当前扩展结点Z是子集树中的内部结点。该结点有x[i]=1和x[i]=0两个儿子结点。其左儿子结点表示x[i]=1的情形,仅当cw+w[i]<=c时才进入左子树,对左子树递归搜索。其右儿子结点表示x[i]=0的情形,因此进入右子树不需要再判断了。
在这里插入图片描述

优化

为了构造最优解,必须在算法中记录与当前最优值相应的当前最优解。为此,在类Loading中增加两个私有数据成员x和bestx。x用于记录从根到当前结点的路径,bestx记录当前最优解。算法搜索到达叶节点处,就修正bestx的值。
递归回溯`

#include <iostream>
using namespace std;
const int n;
//子集树的遍历回溯
void Backtrack(int t)
{
    if(t > n)
        Output(x);
    else
    {
        for(int i = 0;i <= 1;i++)
        {
            //子集树的每个分支只能是0,1
            //类似0-1背包问题
            x[t] = i;
            //满足约束条件且满足界限函数就继续递归
            if(Constrainit(t) && Bound(t))
                Backtrack(t+1);
        }
    }
}
template<class Type>
class Loading
{
    friend Type MaxLoading(Type [],Type,int,int []);
    private:
        void Backtrack(int i);
        int n, //集装箱数
            *x,//当前解
            *bestx;//当前最优解
        Type *w,//集装箱重量数组
            c,//第一艘轮船的载重量
            cw,//当前载重量
            bestw,//当前最优载重量
            r;//剩余集装箱重量
};
template<class Type>
void Loading<Type>::Backtrack(int i)  //搜索第i层结点
{
    if(i > n)  //到达叶节点
    {
        if(cw > bestw)
        {
            for(j = 1;j <= n;j++)
            {
                bestx[j] = x[j];//bestx[]中存储着最优装载路径(0,1元素构成)
            }
        }
        return;
    }
    //搜索子树
    r -= w[i];
    if(cw+w[i] <= c)   //搜索左子树,如果加入之后不超过第一艘床的容量,则可加
    {
        x[i] = 1;
        cw += w[i];
        Backtrack(i + 1);
        cw -= w[i];//回溯
    }
    //这里如果cw+r<=bestw我们就更不能访问右子树了,因为访问右子树表示这个集装箱选择不加入,因此不可能出现>bestw的情况
    if(cw+r > bestw)
    {
        x[i] = 0;
        Backtrack(i+1);
    }
    r += w[i];//回溯
    template<class Type>
    Type MaxLoading(Type w[],Type c,int n,int bestx[])
    {
        Loading<Type> X;
        //初始化X
        X.x = new int [n+1];
        X.w = w;
        X.c = c;
        X.n = n;
        X.bestx = bestx;
        X.cw = 0;
        X.r = 0;
        for(int i = 1;i <= n;i++)
        {
            X.r += x[i];
        }
        X.Backtrack(1);
        delete [] X.x;
        return X.bestw;
    } 
}

迭代回溯

#include <iostream>
using namespace std;
template<class Type>
Type MaxLoading(Type w[],Typec,int n,int bestx[])
{
        int i = 1;
        int *x = new int[n+1];
        Type cw = 0,//当前载重量
            bestw = 0,//当前最优载重量
            r = 0;//剩余集装箱重量
        for(int j = 1;j <=n;j++)
        {
            r+=w[j];
        }
        while(true)
        {
            while(i <= n && !x[i])
            {
                r-=w[i];
                cw+=w[i];
                x[i]=1;
                i++;
            }
            if(i > n)
            {
                for(int j  = 1;j <= n;j++)
                    bestx[j] = x[j];
                bestw = cw;
            }
            else
            {
                r -= w[i];
                x[i] = 0;
                i++;
            }
            while(cw + r <= bestw)
            {
                i--;
                while(i > 0 && !x[i])
                {
                    r += w[i];
                    i--;
                }
                if(i == 0)
                {
                    delete [] x;
                    return bestw;
                }
                //进入右子树
                x[i] = 0;
                cw -= w[i];
                i++;
            }
        }
}

总结

时间复杂度O(2^n)

原创文章 101 获赞 13 访问量 2331

猜你喜欢

转载自blog.csdn.net/weixin_44755413/article/details/105796559
今日推荐