使用分支定价算法求解cuttingstock问题

与上一节比较类似,我们求解的问题还是我们熟悉的cuttingstock问题,之前我们使用列生成算法来求解cuttingstock问题时候虽然求解结果比较接近问题最优解,但是它并不是我们实际需要的整数解,所以我们需要在列生成算法的基础上增加分支定界算法使之成为分支定价算法来求出整数解。

本代码是根据其他博主和cplex自带的例程来编写的。主要的思路就是通过列生成算法来求解原问题的松弛主问题,然后将求解得到的解的第一个非整数解进行分支,然后循环求解,直到所有的解均为整数解就结束。

        IloNum      rollWidth;      //标准钢宽度
        IloNumArray amount(env);    //需要数量
        IloNumArray size(env);      //需要尺寸
        IloNumArray value(env);//当前最优解对应变量值

        readData("cutstock.txt", rollWidth, size, amount);  //读入数据


        //构建主问题模型
        IloModel cutOpt(env);

        //建立主问题目标到主问题模型中
        IloObjective   RollsUsed = IloAdd(cutOpt, IloMinimize(env));

        //给主问题模型增加约束    大于amount数组  小于无穷
        IloRangeArray  Fill = IloAdd(cutOpt, IloRangeArray(env, amount, IloInfinity));

        //声明主问题的决策变量
        IloNumVarArray Cut(env);

        //卷材的种类
        IloInt nWdth = size.getSize();
        //初始化决策变量   以及具体的目标函数和约束
        for (j = 0; j < nWdth; j++) {
            Cut.add(IloNumVar(RollsUsed(1) + Fill[j](int(rollWidth / size[j]))));
        }
        //存储A矩阵 后期加入列
        for (int i = 0; i < nWdth; i++)
        {
            vector<int> p_1;  p_1.clear();
            for (int j = 0; j < nWdth; j++)
            {
                if(i==j)
                    p_1.push_back(rollWidth / size[j]);
                else
                    p_1.push_back(0);

            }
            plan.push_back(p_1);
            p_1.end();
        }
        //for (int i = 0; i < nWdth; i++)
        //{
        //    for (int j = 0; j < nWdth; j++)
        //    {
        //        cout << plan[i][j] << " ";
        //    }
        //    cout << endl;
        //}
        //声明主问题的求解器

        IloCplex cutSolver(cutOpt);
        cutSolver.setOut(env.getNullStream());  //关闭日志记录输出
        //首先求解主问题的结果
        cutSolver.solve();

        //存储当前的主问题模型
        c_model cm;
        cm.obj = cutSolver.getObjValue();
        cm.add_size = 0;
        c_models.push(cm);

        //列生成算法
        /*column_generation(c_models, cutOpt, cutSolver, Fill, Cut, RollsUsed, nWdth, rollWidth, size);
        for (int i = 0; i < plan.size(); i++) {
            for (int j = 0; j < plan[i].size(); j++) {
                cout << plan[i][j] << " , ";
            }
            cout << endl;
        }*/
        while (!c_models.empty())
        {
			//列生成算法
			column_generation(c_models, cutOpt, cutSolver, Fill, Cut, RollsUsed, nWdth, rollWidth, size);
            //首先求解主问题的结果
            cutSolver.solve();
            IloNumArray val(env);
            cutSolver.getValues(Cut, val);
            //寻找非整数变量
            int id = -1;
            for (int i = 0; i < Cut.getSize(); i++) {
                if (val[i] != (int)val[i]) {
                    id = i;
                    break;
                }
            }
            //若当前解为整数解则更新界
            if (id == -1) {
                if (cutSolver.getObjValue() > bound) {
                    bound = cutSolver.getObjValue();
                    value = val;
                    c_models.pop();
                }
                else {
                    c_models.pop();
                }
            }
            //若当前解为非整数解则分两支
            else {
                c_model m1;//左分支
                m1 = c_models.top();//继承父节点所有约束
                m1.branch_id.push_back(id);//分支变量
                m1.lb.push_back(-999999);//定义下界
                int ub = (int)val[id];
                m1.ub.push_back(ub);//定义上界
                m1.get_obj(cutOpt, Cut);//求解当前模型
                m1.add_size++;//记录约束数量
                c_models.push(m1);//压入队列

                c_model m2;//右分支
                m2 = c_models.top();//继承父节点所有约束
                m2.branch_id.push_back(id);//分支变量
                int lb = (int)val[id] + 1;
                m2.lb.push_back(lb);//定义下界
                m2.ub.push_back(999999);//定义上界
                m2.get_obj(cutOpt, Cut);//求解当前模型
                m2.add_size++;//记录约束数量
                c_models.push(m2);//压入队列

                c_models.pop();//弹出队顶模型
            }

        }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值