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

基于OR-Tools的学校拼车问题约束定义技术求助

学校拼车调度OR-Tools建模约束实现示例

我在实际场景中遇到学校拼车调度问题,计划用OR-Tools工具解决。学校分两个校区,部分孩子整周固定去校区1,其余固定去校区2。目前已完成基础模型定义,代码如下:

from ortools.sat.python import cp_model

def main():
    # Data.
    num_parents = 12
    num_children = 20
    num_days = 5
    num_locations = 2
    all_parents = range(num_parents)
    all_children = range(num_children)
    all_days = range(num_days)
    all_locations = range(num_locations)

    # Creates the model.
    model = cp_model.CpModel()
    # Creates ride variables.
    # rides[(n, d, c, l)]: parent 'n' takes child 'c' on day 'd' to location l.
    rides = {}
    for n in all_parents:
        for d in all_days:
            for c in all_children:
                for l in all_locations:
                    rides[(n, d, c, l)] = model.NewBoolVar(f'ride_n{n}d{d}c{c}l{l}')

以下是所有约束的建模实现,重点解决亲子关联和孩子-校区关联的约束:

约束建模实现

1. 每位家长单次行程最多搭载4名孩子(支持按家长调整载客量)

先定义辅助变量标记家长是否执行某校区的行程,再关联载客量限制:

# 辅助变量:parent_trip[(n, d, l)] = 1 表示家长n在d天前往l校区执行一次行程
parent_trip = {}
for n in all_parents:
    for d in all_days:
        for l in all_locations:
            parent_trip[(n, d, l)] = model.NewBoolVar(f'parent_trip_n{n}d{d}l{l}')

# 关联行程与载客:如果家长带某孩子去某校区,说明该行程存在
for n in all_parents:
    for d in all_days:
        for c in all_children:
            for l in all_locations:
                model.AddImplication(rides[(n, d, c, l)], parent_trip[(n, d, l)])

# 单次行程载客量限制:可替换为按家长定义的载客量列表(比如[4,5,3,...])
max_passengers = 4
for n in all_parents:
    for d in all_days:
        for l in all_locations:
            total_kids = sum(rides[(n, d, c, l)] for c in all_children)
            # 行程存在时,载客量不超过上限
            model.Add(total_kids <= max_passengers).OnlyEnforceIf(parent_trip[(n, d, l)])

2. 每位家长单次行程仅能前往一个校区

同一家长同一天的行程,最多对应一个校区:

for n in all_parents:
    for d in all_days:
        # 同一天内,家长前往不同校区的行程数之和 ≤1
        model.Add(sum(parent_trip[(n, d, l)] for l in all_locations) <= 1)

3. 每位家长每日最多执行2次行程(含接送)

这里默认往返算两次行程,限制每日总行程数不超过2:

for n in all_parents:
    for d in all_days:
        model.Add(sum(parent_trip[(n, d, l)] for l in all_locations) <= 2)

4. 家长执行行程时至少搭载一名自己的孩子

先定义亲子关系映射,再添加约束:

# 亲子关系映射:key为家长ID,value为该家长的孩子ID列表(需根据实际数据调整)
parent_child_map = {
    0: [0, 1],
    1: [2, 3],
    2: [4, 5],
    3: [6, 7],
    4: [8, 9],
    5: [10, 11],
    6: [12, 13],
    7: [14, 15],
    8: [16, 17],
    9: [18, 19],
    10: [],  # 若有家长无孩子,需调整约束逻辑
    11: []
}

# 约束:家长执行行程时,必须带至少一名自己的孩子
for n in all_parents:
    # 跳过无孩子的家长,或根据实际需求处理
    if not parent_child_map[n]:
        continue
    for d in all_days:
        for l in all_locations:
            own_kids_count = sum(rides[(n, d, c, l)] for c in parent_child_map[n])
            model.Add(own_kids_count >= 1).OnlyEnforceIf(parent_trip[(n, d, l)])

5. 每个孩子固定前往一个校区,整周地点不变

先定义每个孩子的固定校区,再添加约束确保孩子仅前往该校区:

# 孩子固定校区映射:索引为孩子ID,值为固定校区(0或1,需根据实际数据调整)
child_fixed_location = [0]*10 + [1]*10  # 示例:前10个孩子去校区0,后10个去校区1

# 约束1:孩子只能去固定校区,禁止前往其他校区
for c in all_children:
    fixed_l = child_fixed_location[c]
    for d in all_days:
        for l in all_locations:
            if l != fixed_l:
                # 所有家长都不能带该孩子去非固定校区
                for n in all_parents:
                    model.Add(rides[(n, d, c, l)] == 0)

# 约束2:每个孩子每天必须被恰好一位家长接送(若需求为孩子必须被接送则添加)
for c in all_children:
    for d in all_days:
        fixed_l = child_fixed_location[c]
        model.Add(sum(rides[(n, d, c, fixed_l)] for n in all_parents) == 1)

6. 均衡家长工作量,实现任务公平分配

通过最小化家长总行程次数的差值来实现公平:

# 计算每位家长的总行程次数
parent_total_trips = []
for n in all_parents:
    total = sum(parent_trip[(n, d, l)] for d in all_days for l in all_locations)
    parent_total_trips.append(total)

# 定义最大、最小行程次数变量
max_trips = model.NewIntVar(0, num_days*2, 'max_trips')
min_trips = model.NewIntVar(0, num_days*2, 'min_trips')

model.AddMaxEquality(max_trips, parent_total_trips)
model.AddMinEquality(min_trips, parent_total_trips)

# 目标:最小化最大与最小行程次数的差值,实现工作量均衡
model.Minimize(max_trips - min_trips)

内容的提问来源于stack exchange,提问作者Michael

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 23:27:46