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

保龄球协会赛季赛程自动规划方案开发技术咨询

保龄球协会全赛季赛程规划:优化目标与实现起点

一、确定优化目标与核心约束

硬性约束(必须满足)

  • 排除12月及场馆已预订日期
  • 每队赛季参赛6次(即每个联赛需安排6场比赛)
  • 相邻比赛日间隔为3或4周
  • 大馆最多同时容纳2个联赛,其余2个场馆各容纳1个

优化目标(优先级从高到低)

  1. 最大化大馆利用率:尽可能每次安排2个联赛,减少场地闲置
  2. 均衡联赛赛程分布:让每个联赛的比赛日尽可能均匀间隔,避免扎堆
  3. 均衡场馆使用频次:大馆总使用场次约为其他两馆的2倍(因单次容量为2)

二、OpenOffice Calc 实现步骤

你已整理好可用日期,接下来按以下框架搭建:

1. 整理基础数据表

  • 日期表:列出所有合规日期(排除12月/已预订),新增周间隔列,用DATEDIF(上一日期, 当前日期, "W")计算与上一可用日期的周差,筛选出差值为3或4的日期序列
  • 联赛清单:7个联赛,每个联赛标记6支队伍的参赛次数需求(固定为6)
  • 场馆清单:标记大馆(容量2)和普通馆(容量1)

2. 用规划求解(Solver)做赛程分配

OpenOffice的规划求解是核心工具,配置如下:

  • 目标单元格:计算大馆有效使用率(每安排2个联赛计1分,安排1个计0.5分),设置为最大化
  • 变量单元格:创建二维表(行=合规日期,列=联赛),单元格值为0/1(0=不安排,1=安排);另建表匹配日期-联赛对应的场馆
  • 约束条件:
    • 每个联赛对应的行总和=6(确保赛季6场比赛)
    • 单日期内,大馆对应的联赛数≤2,普通馆≤1
    • 每个联赛的相邻比赛日周差为3或4(用DATEDIF函数验证后添加约束)
    • 所有日期不在禁用列表内

3. 用函数验证约束

用COUNTIF、SUMIF实时校验:

  • SUMIF(赛程表!A:A, "联赛1", 赛程表!B:B):验证联赛1的总比赛次数是否为6
  • DATEDIF(INDEX(赛程表!A:A, MATCH("联赛1", 赛程表!A:A, 0)), 下一个比赛日期, "W"):验证间隔合规性

三、Python 实现起点

如果Calc的规划求解灵活性不足,用Python的整数规划或启发式算法更高效:

1. 依赖库选择

  • 整数规划:pulp(轻量级,易上手)或ortools(适合复杂约束)
  • 日期处理:datetime

2. 核心代码示例

(1)初始化数据

from datetime import datetime, timedelta
import pulp

# 替换为你的日期列表和已预订日期
date_list = [datetime(2024,1,7), datetime(2024,1,28), ...]  # 示例日期
booked_dates = [datetime(2024,2,18)]  # 已预订日期

# 筛选合规日期:排除12月、已预订,且相邻间隔3/4周
available_dates = []
prev_date = None
for d in sorted(date_list):
    if d.month == 12 or d in booked_dates:
        continue
    if prev_date:
        weeks_diff = (d - prev_date).days // 7
        if weeks_diff not in (3,4):
            continue
    available_dates.append(d)
    prev_date = d

leagues = [f"League_{i}" for i in range(1,8)]
venues = {"Big_Venue": 2, "Venue_A":1, "Venue_B":1}  # 场馆:容量

(2)构建优化模型

# 创建最大化问题
prob = pulp.LpProblem("BowlingSchedule", pulp.LpMaximize)

# 变量:(日期, 联赛, 场馆) = 1 表示该组合生效
schedule_vars = pulp.LpVariable.dicts(
    "Schedule",
    [(d, l, v) for d in available_dates for l in leagues for v in venues],
    cat="Binary"
)

# 目标函数:最大化大馆使用率(满负荷计1分,半负荷计0.5分)
prob += pulp.lpSum([schedule_vars[(d,l,"Big_Venue")] for d,l in [(d,l) for d in available_dates for l in leagues]]) * 0.5

# 约束1:每个联赛必须安排6场
for l in leagues:
    prob += pulp.lpSum([schedule_vars[(d,l,v)] for d in available_dates for v in venues]) == 6

# 约束2:单日期单场馆容量不超限
for d in available_dates:
    for v, cap in venues.items():
        prob += pulp.lpSum([schedule_vars[(d,l,v)] for l in leagues]) <= cap

# 约束3:联赛相邻比赛间隔3/4周
for l in leagues:
    # 遍历所有日期对,确保选中的相邻日期间隔合规
    for i in range(len(available_dates)):
        for j in range(i+1, len(available_dates)):
            d1 = available_dates[i]
            d2 = available_dates[j]
            weeks_diff = (d2 - d1).days //7
            if weeks_diff not in (3,4):
                # 若间隔不合规,不能同时选中这两个日期作为该联赛的比赛日
                prob += schedule_vars[(d1,l,"Big_Venue")] + schedule_vars[(d1,l,"Venue_A")] + schedule_vars[(d1,l,"Venue_B")] + \
                        schedule_vars[(d2,l,"Big_Venue")] + schedule_vars[(d2,l,"Venue_A")] + schedule_vars[(d2,l,"Venue_B")] <= 1

(3)求解并输出结果

prob.solve(pulp.PULP_CBC_CMD(msg=0))  # 静默求解

# 输出最终赛程
print("最终赛程:")
for var in prob.variables():
    if var.varValue == 1:
        # 解析变量名
        parts = var.name.split("_")
        date_str = f"{parts[1]}-{parts[2]}-{parts[3]}"
        league = parts[4]
        venue = "_".join(parts[5:])
        print(f"日期:{date_str} | 联赛:{league} | 场馆:{venue}")

3. 备选:贪心算法快速生成初始方案

如果整数规划求解耗时,可先用贪心算法生成初始赛程再调整:

  1. 给每个联赛分配6个符合间隔要求的日期
  2. 对每个日期,优先将联赛分配到大馆,填满后再分配普通馆
  3. 检查冲突(如场馆容量超限),调整日期或场馆分配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 04:57:09