物流运输-最小化门店个数问题
例如,有一批食材要发到肯德基门店,分别分配给了2个司机去送货,如下:
都从上海出发 2个司机 第一个司机: 苏州 新苏站餐厅、园区车坊餐厅 无锡 高铁无锡东站餐厅、无锡城际餐厅 第二个司机: 无锡 海岸城餐厅、无锡城际餐厅、大润发 餐厅 常州 常州百货大楼餐厅、常州城际 餐厅
某位调度大哥一看,都经过无锡,还都要往“无锡城际餐厅”送货,感觉重复了,不能合并?现在2位司机的送货门店个数分别是4个门店以及5个门店,能不能缩减下(由于发现了公共门店,因此缩减门店数变成了可能)
对于这个问题,我们首先不考虑货物能不能装上同一辆车这种容量问题,先考虑门店合并问题,其次考虑容量问题。此文中只说门店合并问题。
和上篇的风格一样,先贴出我们期望的output:
------------原始组合------------- 第一个司机: 4个门店 full: 新苏站餐厅,园区车坊餐厅,高铁无锡东站餐厅,无锡城际餐厅, fixed: 新苏站餐厅,园区车坊餐厅, dynamic: 高铁无锡东站餐厅,无锡城际餐厅, 第二个司机: 5个门店 full: 常州百货大楼餐厅,常州城际 餐厅,海岸城餐厅,无锡城际餐厅,大润发 餐厅, fixed: 常州百货大楼餐厅,常州城际 餐厅, dynamic: 海岸城餐厅,无锡城际餐厅,大润发 餐厅, -------------移动组合------------- 第一个司机(5个门店):新苏站餐厅, 海岸城餐厅, 高铁无锡东站餐厅, 园区车坊餐厅, 大润发 餐厅, 第二个司机(3个门店):常州城际 餐厅, 无锡城际餐厅, 常州百货大楼餐厅, 从第一个司机move到第二个司机的:无锡城际餐厅, 从第二个司机move到第一个司机的:海岸城餐厅, 大润发 餐厅, 第一个司机(3个门店):新苏站餐厅, 无锡城际餐厅, 园区车坊餐厅, 第二个司机(5个门店):常州城际 餐厅, 海岸城餐厅, 高铁无锡东站餐厅, 常州百货大楼餐厅, 大润发 餐厅, 从第一个司机move到第二个司机的:海岸城餐厅, 从第二个司机move到第一个司机的:无锡城际餐厅, 第一个司机(5个门店):新苏站餐厅, 海岸城餐厅, 无锡城际餐厅, 园区车坊餐厅, 大润发 餐厅, 第二个司机(3个门店):常州城际 餐厅, 高铁无锡东站餐厅, 常州百货大楼餐厅, 从第一个司机move到第二个司机的:海岸城餐厅, 从第二个司机move到第一个司机的:海岸城餐厅, 无锡城际餐厅, 大润发 餐厅, 第一个司机(3个门店):新苏站餐厅, 海岸城餐厅, 园区车坊餐厅, 第二个司机(5个门店):常州城际 餐厅, 高铁无锡东站餐厅, 无锡城际餐厅, 常州百货大楼餐厅, 大润发 餐厅, 从第一个司机move到第二个司机的:海岸城餐厅, 无锡城际餐厅, 从第二个司机move到第一个司机的:海岸城餐厅, 第一个司机(3个门店):新苏站餐厅, 园区车坊餐厅, 大润发 餐厅, 第二个司机(5个门店):常州城际 餐厅, 海岸城餐厅, 高铁无锡东站餐厅, 无锡城际餐厅, 常州百货大楼餐厅, 从第一个司机move到第二个司机的:海岸城餐厅, 无锡城际餐厅, 从第二个司机move到第一个司机的:大润发 餐厅, -------------SUMMARY------------- OPTIMAL 有效移动组合个数:5
解题思路:
首先判读这个问题的类型,目测来看数据量较小,不存在穷举非常耗时这种问题,因此首选采用确定性算法,而不是非确定性算法(遗传算法、粒子算法这种是非确定的,每次执行都会变,特别是数据量大的时候)
既然选择确定性算法&&数据量小,因此就穷举组合了,按照上面的城市组合,又分成了固定与动态的城市区分。固定代表这个城市内的门店是不可调整的,动态代表这个城市内的门店是可调整的,可调整代表可以与其他司机的同城市的门店进行互换操作,其实区分固定与动态城市后,算法的数据量会进一步减小,因为只要穷举动态城市部分就行了。
public static void main(String[] args) {
List<ReceiverItem> leftItems=generateData(ListUtils.newList("新苏站餐厅", "园区车坊餐厅"), ListUtils.newList("高铁无锡东站餐厅", "无锡城际餐厅"));
List<ReceiverItem> rightItems=generateData(ListUtils.newList("常州百货大楼餐厅", "常州城际 餐厅"), ListUtils.newList("海岸城餐厅", "无锡城际餐厅", "大润发 餐厅"));
List<IntVar> leftFixedVars=new ArrayList<>();
List<IntVar> leftExchangableVars=new ArrayList<>();
List<IntVar> rightFixedVars=new ArrayList<>();
List<IntVar> rightExchangableVars=new ArrayList<>();
CpModel model = new CpModel();
//第一个司机
for(ReceiverItem receiverItem:leftItems)
{
if(!receiverItem.exchangable)
{
//不可以交换,因此固定为第一个司机
IntVar var=model.newConstant(1);
leftFixedVars.add(var);
}
else
{
IntVar var=model.newIntVar(1, 2, "");
leftExchangableVars.add(var);
}
}
//第二个司机
for(ReceiverItem receiverItem:rightItems)
{
if(!receiverItem.exchangable)
{
//不可以交换,因此固定为第二个司机
IntVar var=model.newConstant(2);
rightFixedVars.add(var);
}
else
{
IntVar var=model.newIntVar(1, 2, "");
rightExchangableVars.add(var);
}
}
VarArraySolutionPrinter varArraySolutionPrinter=new VarArraySolutionPrinter(leftItems, rightItems, leftFixedVars, leftExchangableVars, rightFixedVars, rightExchangableVars);
CpSolver solver = new CpSolver();
System.out.println("------------原始组合-------------");
PrintUtils.display组合("第一个司机:", leftItems);
PrintUtils.display组合("第二个司机:", rightItems);
System.out.println("-------------移动组合-------------");
CpSolverStatus status=solver.searchAllSolutions(model, varArraySolutionPrinter);
System.out.println("-------------SUMMARY-------------");
System.out.println(status);
List<MoveAction> moveActions=varArraySolutionPrinter.getMoveActions();
System.out.println("有效移动组合个数:"+moveActions.size());
}
static class VarArraySolutionPrinter extends CpSolverSolutionCallback {
private List<ReceiverItem> leftItems;
private List<ReceiverItem> rightItems;
private List<IntVar> leftFixedVars;
private List<IntVar> leftExchangableVars;
private List<IntVar> rightFixedVars;
private List<IntVar> rightExchangableVars;
private List<MoveAction> moveActions=new ArrayList<>();
public List<MoveAction> getMoveActions()
{
return this.moveActions;
}
public VarArraySolutionPrinter(List<ReceiverItem> leftItems, List<ReceiverItem> rightItems, List<IntVar> leftFixedVars, List<IntVar> leftExchangableVars, List<IntVar> rightFixedVars, List<IntVar> rightExchangableVars) {
this.leftItems=leftItems;
this.rightItems=rightItems;
this.leftFixedVars=leftFixedVars;
this.leftExchangableVars=leftExchangableVars;
this.rightFixedVars=rightFixedVars;
this.rightExchangableVars=rightExchangableVars;
}
@Override
public void onSolutionCallback() {
Set<String> leftReceivers=new HashSet<>();
Set<String> rightReceivers=new HashSet<>();
List<String> originalleft=new ArrayList<>();
List<String> move2left=new ArrayList<>();
List<String> originalright=new ArrayList<>();
List<String> move2right=new ArrayList<>();
//left fixed
for(int idx=0;idx<leftFixedVars.size();idx++)
{
leftReceivers.add(leftItems.get(idx).name);
}
//left exchangable
for(int idx=0;idx<leftExchangableVars.size();idx++)
{
IntVar var=leftExchangableVars.get(idx);
long v=value(var);
if(v==1)
{
leftReceivers.add(leftItems.get(idx+leftFixedVars.size()).name);
originalleft.add(leftItems.get(idx+leftFixedVars.size()).name);
}
}
for(int idx=0;idx<rightExchangableVars.size();idx++)
{
IntVar var=rightExchangableVars.get(idx);
long v=value(var);
if(v==1)
{
leftReceivers.add(rightItems.get(idx+rightFixedVars.size()).name);
move2left.add(rightItems.get(idx+rightFixedVars.size()).name);
}
}
//right fixed
for(int idx=0;idx<rightFixedVars.size();idx++)
{
rightReceivers.add(rightItems.get(idx).name);
}
//right exchangable
for(int idx=0;idx<leftExchangableVars.size();idx++)
{
IntVar var=leftExchangableVars.get(idx);
long v=value(var);
if(v==2)
{
rightReceivers.add(leftItems.get(idx+leftFixedVars.size()).name);
move2right.add(rightItems.get(idx+rightFixedVars.size()).name);
}
}
for(int idx=0;idx<rightExchangableVars.size();idx++)
{
IntVar var=rightExchangableVars.get(idx);
long v=value(var);
if(v==2)
{
rightReceivers.add(rightItems.get(idx+rightFixedVars.size()).name);
originalright.add(rightItems.get(idx+rightFixedVars.size()).name);
}
}
if(leftReceivers.size()<=5&&rightReceivers.size()<=5&&(leftReceivers.size()<=3||rightReceivers.size()<=3))//最终穷举出来的要满足的约束
{
if(move2left.size()>0&&move2right.size()>0) {
PrintUtils.displayReceivers(leftReceivers, rightReceivers, "");
PrintUtils.displayActions(originalleft, move2left, originalright, move2right);
MoveAction moveAction = new MoveAction();
moveAction.setMove2Left(move2left);
moveAction.setMove2Right(move2right);
moveActions.add(moveAction);
}
}
}
}
自省推动进步,视野决定未来。
心怀远大理想。
为了家庭幸福而努力。
商业合作请看此处:https://www.magicube.ai
心怀远大理想。
为了家庭幸福而努力。
商业合作请看此处:https://www.magicube.ai