物流运输-最小化门店个数问题

例如,有一批食材要发到肯德基门店,分别分配给了2个司机去送货,如下: 

1
2
3
4
5
6
7
8
9
10
11
都从上海出发
2个司机
 
第一个司机:
    苏州  新苏站餐厅、园区车坊餐厅
    无锡  高铁无锡东站餐厅、无锡城际餐厅
 
 
第二个司机:
    无锡  海岸城餐厅、无锡城际餐厅、大润发 餐厅
    常州  常州百货大楼餐厅、常州城际 餐厅  

 

某位调度大哥一看,都经过无锡,还都要往“无锡城际餐厅”送货,感觉重复了,不能合并?现在2位司机的送货门店个数分别是4个门店以及5个门店,能不能缩减下(由于发现了公共门店,因此缩减门店数变成了可能)

对于这个问题,我们首先不考虑货物能不能装上同一辆车这种容量问题,先考虑门店合并问题,其次考虑容量问题。此文中只说门店合并问题。

和上篇的风格一样,先贴出我们期望的output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
------------原始组合-------------
第一个司机:       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  

 

 解题思路:

首先判读这个问题的类型,目测来看数据量较小,不存在穷举非常耗时这种问题,因此首选采用确定性算法,而不是非确定性算法(遗传算法、粒子算法这种是非确定的,每次执行都会变,特别是数据量大的时候)

既然选择确定性算法&&数据量小,因此就穷举组合了,按照上面的城市组合,又分成了固定与动态的城市区分。固定代表这个城市内的门店是不可调整的,动态代表这个城市内的门店是可调整的,可调整代表可以与其他司机的同城市的门店进行互换操作,其实区分固定与动态城市后,算法的数据量会进一步减小,因为只要穷举动态城市部分就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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);
                }
            }
        }
    }

  

 

posted @   McKay  阅读(299)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示