如何扩展道路维护示例以支持多Crew分配至同一Job?
问题描述
基于道路维护示例扩展作业(Job)与班组(Crew)的关联关系,支持将多个Crew分配至同一Job(用于加快进度、提升优先级)。尝试将Job.crew从单例改为List<Crew>,将@PlanningVariable替换为@PlanningListVariable,并初始化非空List后,出现报错:基础规划变量和列表规划变量不能在同一实体类中使用。
解决方案
OptaPlanner不允许同一个规划实体类同时存在普通@PlanningVariable和@PlanningListVariable,可以通过以下两种方式解决:
方案1:移除Job类中的其他普通规划变量(仅保留列表规划变量)
如果你的Job类中原本只有crew这一个规划变量,检查是否存在其他被@PlanningVariable注解标记的字段,将它们全部移除或改为非规划变量(作为问题事实的固定属性)。之后确保Job类仅保留@PlanningListVariable标记的List<Crew>字段:
@PlanningEntity public class Job { // 其他固定属性(非规划变量) private String jobId; private Duration duration; // 列表规划变量:支持分配多个班组 @PlanningListVariable(valueRangeProviderRefs = "crewRange") private List<Crew> crewList = new ArrayList<>(); // getter、setter、构造方法 }
这种方式适合Job仅需多班组分配这一项规划需求的场景。
方案2:引入中间分配实体(更通用的多分配场景)
如果Job类中存在其他必须保留的普通规划变量(比如作业开始时间startTime),推荐通过新增中间实体来实现多班组分配:
- 调整Job为问题事实:移除
Job类上的@PlanningEntity注解,将其作为固定的问题数据(不再是规划实体)。 - 创建JobCrewAssignment规划实体:该实体代表“班组分配到作业”的关联关系,每个实例对应一个班组和一个作业:
@PlanningEntity public class JobCrewAssignment { private Long assignmentId; // 关联的作业(问题事实,非规划变量) private Job job; // 规划变量:分配的班组 @PlanningVariable(valueRangeProviderRefs = "crewRange") private Crew crew; // getter、setter、构造方法 }
- 更新规划解决方案类:将原有的Job规划实体集合替换为JobCrewAssignment集合,同时保留所有问题事实(Job列表、Crew列表):
@PlanningSolution public class RoadMaintenanceSolution { @ValueRangeProvider(id = "crewRange") private List<Crew> crewList; // 问题事实:所有待处理的作业 private List<Job> jobList; // 规划实体:所有班组-作业的分配关系 @PlanningEntityCollectionProperty private List<JobCrewAssignment> assignmentList; // 分数计算器、getter、setter、构造方法 }
这种方式的优势是灵活支持复杂约束(比如限制同一班组在同一时间不能分配到多个作业、控制单个作业的最大班组数量等),也不会影响Job类原有的其他属性。
补充说明
如果选择方案2,需要在分数约束中添加逻辑:
- 根据业务需求设置单个作业的最大班组分配数量
- 避免班组的时间冲突(如果作业有时间维度的话)
内容的提问来源于stack exchange,提问作者user1116536
相关产品推荐
相关产品推荐

