[回溯][C++]装载问题 输出最佳方案

问题描述

有一批共n个集装箱要装上2艘载重量分别为c_1c_2的轮船,其中集装箱i的重量为w_i,且\sum_{i=1}^n w_i\leq c_1+c_2;要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。如果有,找出一种装载方案。

\left\{\begin{matrix} max\sum_{i=1}^n w_i x_i \\ s.t.\sum_{i=1}^n w_i x_i \leqslant cl \end{matrix}\right. x_i \in \begin{Bmatrix} 0,1 \end{Bmatrix} ,1\leq i\leq n

输入:船的载重C1,集装箱个数n以及各个集装箱的重量W=<w1,w2,…,wn>

输出:一种装载方案,即:第一艘船上装入哪些集装箱

为了提高内存利用率,下面代码将从数组0位开始,最后输出结果进行+1操作

注意:此文章所示代码不考虑剪枝 (优化) 操作

递归回溯

代码
#include <iostream>
using namespace std;

// 工作流类
class Work
{
public:
    // 载重
    int c = 0;

    // 现重
    int c_now = 0;

    // 最有重量
    int bestW = 0;

    // 集装箱数量
    int n = 0;

    // 集装箱重量
    const int *w = NULL;

    // 最优装载方案
    bool *bestWays = NULL;

    // 目前选择情况
    bool *nowWays = NULL;

    // 构造
    Work(const int *_w, const int _n, const int _c)
    {
        // 接收传入的参数
        w = _w;
        n = _n;
        c = _c;

        // 初始化
        bestWays = new bool[n]{false};
        nowWays = new bool[n]{false};

        // 计算最优解
        loading(0);
    }

    // 析构
    ~Work()
    {
        delete[] bestWays;
        delete[] nowWays;
    }

    // 构造最优解-递归实现
    void loading(int i)
    {
        // 检查是否到达末尾
        if (i >= n)
        {
            // 检查当前方案是否为最优方案
            if (c_now > bestW)
            {
                // 当前为最佳方案
                bestW = c_now;
                memcpy(bestWays, nowWays, sizeof(bool) * n);
            }
            return;
        }

        // 判断当前集装箱能否被装载
        if (c_now + w[i] <= c)
        {
            // 当前集装箱能被装载,进入情况
            c_now += w[i];
            nowWays[i] = true;
            loading(i + 1);
            // 撤销装载
            c_now -= w[i];
            nowWays[i] = false;
        }

        // 判断不包含此物品的情况
        loading(i + 1);
    }
};

int main()
{
    // 所需变量
    int c1, c2, *w, n, sum = 0;

    // 输入
    cout << "c1: ";
    cin >> c1;
    cout << "c2: ";
    cin >> c2;
    cout << "n: ";
    cin >> n;
    w = new int[n]{0};
    cout << "w: ";
    for (int i = 0; i < n; i++)
    {
        cin >> w[i];
        sum += w[i];
    }

    // 计算最优解
    Work work(w, n, c1);

    // 输出结果
    // 判断剩余集装箱能都被第二艘船装载
    if (sum - work.bestW <= c2)
    {
        // 能被装载
        cout << "can load,\nship1: ";
        for (int i = 0; i < n; i++)
            if (work.bestWays[i] == true)
                cout << i + 1 << " ";
        cout << "\nship2: ";
        for (int i = 0; i < n; i++)
            if (work.bestWays[i] == false)
                cout << i + 1 << " ";
    }
    else
    {
        //不能被装载
        cout << "cannot load";
    }

    return 0;
}
运行结果
c1: 152
c2: 130
n: 7
w: 90 80 40 30 20 12 10
can load,
ship1: 1 3 6 7
ship2: 2 4 5

迭代回溯

代码
#include <iostream>
using namespace std;

// 工作流类
class Work
{
public:
    // 载重
    int c = 0;

    // 现重
    int c_now = 0;

    // 最有重量
    int bestW = 0;

    // 集装箱数量
    int n = 0;

    // 集装箱重量
    const int *w = NULL;

    // 最优装载方案
    bool *bestWays = NULL;

    // 目前选择情况
    bool *nowWays = NULL;

    // 构造
    Work(const int *_w, const int _n, const int _c)
    {
        // 接收传入的参数
        w = _w;
        n = _n;
        c = _c;

        // 初始化
        bestWays = new bool[n]{false};
        nowWays = new bool[n]{false};

        // 计算最优解
        loading();
    }

    // 析构
    ~Work()
    {
        delete[] bestWays;
        delete[] nowWays;
    }


    // 构造最优解-迭代实现
    void loading()
    {
        // 当前层级
        int i = 0;

        // 循环退出标记
        bool run = true;

        // 状态(路径)记录
        int *x = new int[n]{0};
        while (run)
        {
            // 检查是否到达末尾
            if (i >= n)
            {
                // 检查当前方案是否为最优方案
                if (c_now > bestW)
                {
                    // 当前为最佳方案
                    bestW = c_now;
                    memcpy(bestWays, nowWays, sizeof(bool) * n);
                }
                i--;
                continue;
            }

            //判断当前状态
            switch (x[i])
            {
            case 0:
                // 初状态
                //  判断当前集装箱能否被装载
                if (c_now + w[i] <= c)
                {
                    // 当前集装箱能被装载,进入情况
                    c_now += w[i];
                    nowWays[i] = true;
                    x[i] = 1;
                    i++;
                    x[i] = 0;
                    break;
                }
                x[i] = 2;
                break;
            case 1:
                // 遍历完左子树状态
                //  撤销装载
                c_now -= w[i];
                nowWays[i] = false;
                x[i] = 2;
                break;
            case 2:
                // 未遍历左子树的状态
                x[i] = 3;
                i++;
                x[i] = 0;
                break;
            case 3:
                // 末状态
                if (i == 0)
                    return;
                i--;
                break;
            default:
                return;
            }
        }
    }
};

int main()
{
    // 所需变量
    int c1, c2, *w, n, sum = 0;

    // 输入
    cout << "c1: ";
    cin >> c1;
    cout << "c2: ";
    cin >> c2;
    cout << "n: ";
    cin >> n;
    w = new int[n]{0};
    cout << "w: ";
    for (int i = 0; i < n; i++)
    {
        cin >> w[i];
        sum += w[i];
    }

    // 计算最优解
    Work work(w, n, c1);

    // 输出结果
    // 判断剩余集装箱能都被第二艘船装载
    if (sum - work.bestW <= c2)
    {
        // 能被装载
        cout << "can load,\nship1: ";
        for (int i = 0; i < n; i++)
            if (work.bestWays[i] == true)
                cout << i + 1 << " ";
        cout << "\nship2: ";
        for (int i = 0; i < n; i++)
            if (work.bestWays[i] == false)
                cout << i + 1 << " ";
    }
    else
    {
        // 不能被装载
        cout << "cannot load";
    }

    return 0;
}
运行结果
c1: 152
c2: 130
n: 7
w: 90 80 40 30 20 12 10
can load,
ship1: 1 3 6 7
ship2: 2 4 5

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值