You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 03:01:09