与上一节比较类似,我们求解的问题还是我们熟悉的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();//弹出队顶模型
}
}