OR-Tools护士排班连续班次约束失效问题求助
解决OR-Tools护士排班的连续班次约束问题
你的核心问题在于现有约束逻辑错误,强制护士必须从最早班次开始连续工作,而非允许任意连续的工作区间,同时代码缩进问题导致约束未正确应用。以下是两种可行的修复方案:
方案一:用线性约束避免班次断档
这是最简洁的实现方式,通过约束确保不会出现"跳过中间班次"的情况:
# 核心约束:禁止出现中间断档的工作班次 for n in all_nurses: for d in all_days: # 遍历所有中间班次(跳过第一个和最后一个班次) for s in range(1, len(all_shifts)-1): # 逻辑:如果s-1和s+1班次都工作,那么s班次必须工作 # 等价于:shifts[n,d,s-1] + shifts[n,d,s+1] <= 1 + shifts[n,d,s] model.Add(shifts[(n, d, s-1)] + shifts[(n, d, s+1)] <= 1 + shifts[(n, d, s)]) # 可选:添加每日最大工作班次约束(根据你的需求调整数值) max_daily_shifts = 2 for n in all_nurses: for d in all_days: model.Add(sum(shifts[(n, d, s)] for s in all_shifts) <= max_daily_shifts)
约束说明
这个线性约束直接阻断了"同时上1和3班、但不上2班"的情况:当s=2时,若shifts[1]和shifts[3]都为1,左边和为2,右边1+shifts[2]若为1则不满足约束,从而禁止这种断档情况;而连续班次(如1和2班)则完全符合约束要求。
方案二:用辅助变量跟踪工作区间(更易扩展)
如果需要后续添加与班次区间相关的约束(如首班次时间限制),可以用辅助变量明确跟踪每个护士每天的首/尾工作班次:
# 创建首班次、尾班次变量(-1表示当日不上班) first_shift = {} last_shift = {} for n in all_nurses: for d in all_days: shift_count = len(all_shifts) first_shift[(n, d)] = model.NewIntVar(-1, shift_count-1, f"first_shift_n{n}_d{d}") last_shift[(n, d)] = model.NewIntVar(-1, shift_count-1, f"last_shift_n{n}_d{d}") # 约束1:若护士上班次s,则s必须在首/尾班次区间内 for n in all_nurses: for d in all_days: for s in all_shifts: model.Add(first_shift[(n,d)] <= s).OnlyEnforceIf(shifts[(n,d,s)]) model.Add(s <= last_shift[(n,d)]).OnlyEnforceIf(shifts[(n,d,s)]) # 约束2:首/尾班次必须被实际工作 for n in all_nurses: for d in all_days: model.Add(shifts[(n, d, first_shift[(n,d)])] == 1).OnlyEnforceIf(first_shift[(n,d)] != -1) model.Add(shifts[(n, d, last_shift[(n,d)])] == 1).OnlyEnforceIf(last_shift[(n,d)] != -1) # 约束3:首班次<=尾班次,且不上班时首尾班次都为-1 for n in all_nurses: for d in all_days: model.Add(first_shift[(n,d)] <= last_shift[(n,d)]) model.Add(first_shift[(n,d)] == -1).OnlyEnforceIf(last_shift[(n,d)] == -1) model.Add(last_shift[(n,d)] == -1).OnlyEnforceIf(first_shift[(n,d)] == -1) # 约束4:首尾班次之间的所有班次必须工作 for n in all_nurses: for d in all_days: for s in all_shifts: in_range = model.NewBoolVar(f"in_range_n{n}_d{d}_s{s}") # 定义"班次s在首尾区间内"的逻辑 model.Add(first_shift[(n,d)] <= s).OnlyEnforceIf(in_range) model.Add(s <= last_shift[(n,d)]).OnlyEnforceIf(in_range) model.Add(first_shift[(n,d)] > s).OnlyEnforceIf(in_range.Not()) model.Add(s > last_shift[(n,d)]).OnlyEnforceIf(in_range.Not()) # 若在区间内则必须上班 model.Add(shifts[(n,d,s)] == 1).OnlyEnforceIf(in_range)
方案优势
这种方式更直观地表达了"连续班次"的逻辑,同时方便后续扩展约束(比如限制护士的首班次不能早于某个时间,尾班次不能晚于某个时间)。
内容的提问来源于stack exchange,提问作者Gabriel Rips
相关产品推荐
相关产品推荐

