回溯法求解装载问题

最优装载问题

问题描述
有一批共n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且集装箱的重量和<=c1+c2。装载问题要求确定,是否有一个合理的装载方案可将这n个集装箱装上这2艘轮船,如果有,找出一种装载方案。
例如,当n=3,c1=c2=50,且w=[10,40,40]时,可将集装箱1和2装上第1艘轮船,而将集装箱3装上第2艘轮船;如果w=[20,40,40],则无法将这3个集装箱都装上轮船。
当集装箱重量和=c1+c2时,装载问题等价于子集和问题。当c1=c2且重量和=2c1时,装载问题等价于集合划分问题。即使限制wi为整数,c1和c2也是整数。子集和问题与划分问题都是NP难的。由此可知,装载问题也是NP难的。
容易证明,如果一个给定的装载问题有解,则采用下面的策略可以得到最优装载方案:
先将第一艘轮船尽可能装满,然后将剩余的集装箱装上第二艘轮船。将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近c1。
所以现在问题转变为一个特殊的0-1背包问题。
算法设计
用回溯法求解装载问题,用子集树表示其解空间。
可行性约束函数:剪去不满足重量和<=c1的子树
上界函数:剪去不含最优解的子树,此处使用cw+r即当前重量加剩余集装箱的重量,如果≤bestw则剪去
算法Backtrack(i):当i>n时,算法搜索至叶结点,其相应的装载重量为cw。如果cw>bestw,则表示当前解优于当前最优解,更新bestw;当i<=n时,扩展结点是Z中的内部节点。左子树检查可行性,可行则进入;右子树检查是否有最优解。
代码示例
//回溯法常规类
template<class Type>
class Loading{
    friend Type MaxLoading(Type [],Type,int);
private:
    void Backtrack(int i);
    int n;
    Type *w,					//集装箱数目
    	  c,					//限重
    	  cw,					//当前重量
    	  bestw,				//最优重量
    	  r;					//重量上界
};

template<class Type>
void Loading<Type>::Backtrack(int i){
    if(i>n){
        bestw=cw;
        return;
    }
    //搜索子树
    r-=w[i];
    if(cw+w[i]<=c){
        cw+=w[i];
        Backtrack(i+1);
        cw-=w[i];
    }
    if(cw+r>bestw)
        Backtrack(i+1);
    r+=w[i];
}
测试推荐
洛谷真题:https://www.luogu.com.cn/problem/P1049

AC代码

#include<iostream>
#define maxsize 31
using namespace std;
template<class Type>
class Loading{
    friend int main(void);
private:
    void Backtrack(int i);
    int n;
    Type w[maxsize],					
    	  c,				
    	  cw,				
    	  bestw,				
    	  r;					
};

template<class Type>
void Loading<Type>::Backtrack(int i){
    if(i>n){
        bestw=cw;
        return;
    }
    r-=w[i];
    if(cw+w[i]<=c){
        cw+=w[i];
        Backtrack(i+1);
        cw-=w[i];
    }
    if(cw+r>bestw)
        Backtrack(i+1);
    r+=w[i];
}

int main()
{
    Loading<int> box;
    cin>>box.c;
    cin>>box.n;
    box.r=0;
    for(int i=1;i<=box.n;i++){cin>>box.w[i]; box.r+=box.w[i];}
    box.cw=0;
    box.bestw=0;
    box.Backtrack(1);
    cout<<box.c-box.bestw;
}
算法分析

解空间树中共2n个结点,因此时间复杂度为O(2n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值