装载问题-回溯法

问题描述

 有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且
          在这里插入图片描述

问题:
是否有一个合理的装载方案,可将这n个集装箱装上这2艘轮船?如果有,找出一种装载方案。

例如:当n=3, c1=c2=50

(1)若w=[10, 40, 40]

   可将集装箱1和集装箱2装上第一艘轮船,而将集装箱3装上第二艘轮船;

(2)如果w=[20, 40, 40]

   则无法将这3个集装箱都装上船;



基本思路

 已证明,如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。

  1. 首先将第一艘轮船尽可能装满;
  2. 将剩余的集装箱装上第二艘轮船。

  将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近c1。由此可知,装载问题等价于以下特殊的0-1背包问题。



算法思路

表示解空间,则解为n元向量{x1, … ,xn }, xi∈{0, 1} 。

约束条件

 当前搜索的层i <= n时,当前扩展结点Z为子集树的内部结点,仅当满足cw+w[i] <= c时进入左子树,x[i]=1; 当cw+w[i] > c ,在以结点Z为根的子树中所有结点都不满足约束条件,因而该子树中解都是不可行解,因而将在该子树删去。

限界函数

 由于是最优化问题, 可利用最优解性质进一步剪去不含最优解的子树:
 设Z是解空间树第i层上的当前扩展结点。
 设     bestw: 当前最优载重量,
        cw : 当前扩展结点Z的载重量 ;
       r : 剩余集装箱的重量;
 在以Z为根的子树中任意叶结点所相应的载重量不超过cw + r。因此,当cw + r (限界函数) ≤ bestw时,可将Z的右子树剪去。即:cw + r > bestw 时搜索右子树,x[i]=0;


例子

在这里插入图片描述



演示分析过程
在这里插入图片描述


核心代码

template<classType>
void Loading<Type>::Backtrack(int i)
{ / /搜索第i层结点
 	if (i>n) {//到达叶结点
         bestw=cw;
         return}
     r - = w[i];  //搜索子树
     if (cw+w[i]<=c){ //搜索左子树
		x[i]=1;
		cw += w[i];    
		Backtrack (i+1);
		cw - = w[i]; 
     }
     if (cw+r > bestw){ //搜索右子树
		x[i]=0Backtrack(i+1)}
  	 r+=w[i] 

代码

#include <bits/stdc++.h>
using namespace std;
int n; //集装箱数
int cw; // 当前载重量, current weight
int bestw; //最优载重重量
int r;  //剩余集装箱重量
int c1; //第一艘轮船的载重量
int c2; //第二艘轮船的载重量
int x[100]; //当前解
int bestx[100]; //当前最优解
int w[100]; //集装箱重量数组
void OutPut()
{
    int restweight = 0;
    for(int i = 1; i <= n; ++i)
        if(bestx[i] == 0)
           restweight += w[i];
    if(restweight > c2)
        cout<<"不能装入"<<endl;
    else
    {
        cout<<"船1装入的货物为:";
        for(int i = 1; i <= n; ++i)
            if(bestx[i] == 1)
               cout<<" "<<i;
        cout<<endl<<"船2装入的货物为:";
        for(int i = 1; i <= n; ++i)
            if(bestx[i] != 1)
               cout<<" "<<i;

    }
}
void BackTrack(int i)
{
    if(i > n)
    {
        if(cw > bestw)
        {
            for(int i = 1; i <= n; ++i)
                bestx[i] = x[i];
            bestw = cw;
        }
        return;
    }
    r -= w[i];
    if(cw + w[i] <= c1) //约束条件
    {
        cw += w[i];
        x[i] = 1;
        BackTrack(i + 1);
        x[i] = 0;
        cw -= w[i];
    }
    if(cw + r > bestw) //限界函数
    {
        x[i] = 0;
        BackTrack(i + 1);
    }
    r += w[i];

}
void Initialize()
{
    bestw = 0;
    r = 0;
    cw = 0;
    for(int i = 1; i <= n; ++i)
        r += w[i];
}
void InPut()
{
    cout<<"请输入箱子个数:";
    cin>>n;

    cout<<"请输入两艘船的最大载重量:";
    cin>>c1>>c2;

    cout<<"请输入箱子的重量:";
    for(int i = 1; i <= n; ++i)
       cin>>w[i];

}
int main()
{
    InPut();
    Initialize();
    BackTrack(1);
    OutPut();
}


测试样例

输入
请输入箱子个数:4
请输入两艘船的最大载重量:100 100
请输入箱子的重量:90 10 80 10

输出
船1装入的货物为: 1 2
船2装入的货物为: 3 4
在这里插入图片描述


输入
请输入箱子个数:4
请输入两艘船的最大载重量:100 100
请输入箱子的重量:90 20 90 10

输出
不能装入
在这里插入图片描述

  • 51
    点赞
  • 273
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值