装载问题(最优装载问题变形)-回溯法-深度搜索

问题描述:

有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且∑wi <= c1 + c2。

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

问题分析:

如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。
(1)首先将第一艘轮船尽可能装满;
(2)将剩余的集装箱装上第二艘轮船。

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

 max∑ wi * xi && ∑ wi * xi <= c1, xi ∈ {0, 1}, 1 <= i <= n;

算法思路:

排序树表示解空间,则解为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;

代码:

#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)
        printf("不能装入\n");
    else
    {
        printf("船1装入的货物为:");
        for(int i = 1; i <= n; ++i)
            if(bestx[i] == 1)
               printf(" %d", i);
        printf("\n船2装入的货物为:");
        for(int i = 1; i <= n; ++i)
            if(bestx[i] != 1)
               printf(" %d", 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()
{
    scanf("%d", &n);
    scanf("%d %d", &c1, &c2);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &w[i]);
}
int main()
{
    InPut();
    Initialize();
    BackTrack(1);
    OutPut();
}

测试样例:

测试样例一:

输入:

3

50 50

10 40 40

输出

船1装入的货物为: 1 2

船2装入的货物为: 3

截图:


测试样例2:

输入:

3

50 50

20 40 40

输出:

不能装入

截图:


猜你喜欢

转载自blog.csdn.net/m0_38015368/article/details/80196634