Based on my previous question How to choose a vehicle that can accommodate the specified number of employees with OptaPlanner?
Employee:
public class Employee {
@PlanningId
private Long id;
private String name;
@PlanningVariable(valueRangeProviderRefs = "transportRange")
private Transport transport;
}
Transport:
@PlanningEntity
public class Transport {
@PlanningId
private Long id;
private Integer capacity;
@PlanningListVariable(valueRangeProviderRefs = "employeeRange")
private List<Employee> employeeList = new ArrayList<>();
public int countEmptySeats() {
return seatsNumber - inspectors.size();
}
}
@PlanningEntity
class Transport {
@PlanningId
Long id;
@InverseRelationShadowVariable(sourceVariableName = "transport")
List<Employee> employees = new ArrayList<>();
}
Solution:
@PlanningSolution
public class Solution {
@PlanningEntityCollectionProperty
private List<Transport> transports;
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "employeeRange")
private List<Employee> employees;
@PlanningScore
private HardMediumSoftLongScore score;
public Plan(List<Transport> transports, List<Employee> employees) {
this.transports = transports;
this.employees = employees;
}
}
Constraints:
private Constraint transportMaxCapacity(ConstraintFactory factory) {
return factory.forEach(Transport.class)
.filter(transport -> transport.getEmployees().size() > transport.getCapacity())
.penalizeLong("max capacity conflict",
HardMediumSoftLongScore.ONE_HARD, transport -> transport.getEmployees().size() - transport.getCapacity());
}
private Constraint transportCapacity(ConstraintFactory factory) {
return factory.forEach(Transport.class)
.filter(transport -> !transport.getEmployees().isEmpty())
.filter(transport -> transport.getEmployees().size() < transport.getCapacity())
.penalizeLong("optimal placement conflict",
HardMediumSoftLongScore.ONE_SOFT, transport -> transport.getCapacity() - transport.getInspectorList().size());
}
private Constraint optimalVehicleConflict(ConstraintFactory factory) {
return factory.forEach(Transport.class)
.filter(transport -> transport.getEmployees().isEmpty())
.penalize("Empty transport", HardMediumSoftLongScore.ONE_SOFT, vehicle -> (int) Math.pow(transport.countEmptySeats(), 2));
}
The problem: When I have vehicle with capacities: 17, 6, 2, 2, 2 and 6 passengers then as a result I get 3 vehicles with 2 passengers instead of 1 vehicle with capacity 6.
+---------+-----------+
| vehicle | employees |
+---------+-----------+
| 17 | - |
| 6 | - |
| 2 | 2 |
| 2 | 2 |
| 2 | 2 |
+---------+-----------+
How I can solve this problem? Thanks in advance!