
1. 赛题核心与破题思路拆解刚拿到第五届长三角高校数学建模竞赛A题的时候我第一反应是这题有点意思它不像传统建模题那样直接给你一堆数据和明确的问题。今年的A题从题目描述来看更像是一个“问题定义方案设计”的综合体考察的不仅仅是模型建立和求解能力更关键的是问题抽象、需求分析和系统化建模的能力。很多队伍可能会在这里栽跟头一上来就急着找算法、写代码结果发现方向完全跑偏。这道题的核心我理解是要求我们为一个复杂的现实系统比如城市交通流、物流配送网络、信息传播网络等构建一个动态评估与优化模型。题目通常会给出一个系统的初始状态、一些运行规则或约束条件以及几个阶段性的目标。我们的任务不是简单地预测未来而是设计一套“驾驶舱”系统能够实时评估系统健康度并在关键节点给出干预策略以引导系统朝着最优或较优的状态演进。为什么这么理解从历年长三角赛题和“数学建模”这个关键词来看它必然涉及数学工具微分方程、图论、优化理论、统计分析等的应用。从“解题思路及代码”这个后缀来看它要求可落地的方案不是纯理论探讨。因此破题的第一步一定是深度解读题目背景把那些描述性的、定性的要求转化为清晰的、可量化的数学问题。我的整体思路框架分为四步系统界定与关键要素提取明确系统边界识别出核心的实体如节点、个体、车辆、属性如状态、容量、成本和交互关系如流动、竞争、合作。指标体系构建根据题目最终目标如效率最高、成本最低、公平性最好拆解出多层级的评估指标。例如总效率可能由吞吐量、平均延迟、资源利用率等次级指标构成。动态模型建立用数学语言描述系统随时间的演化。这可能是一个微分方程组连续型、一个差分方程或状态转移矩阵离散型或者一个基于智能体的模拟模型。这一步是核心模型要能反映主要矛盾又不能过于复杂导致无法求解。策略优化与仿真验证在建立的模型基础上将“给出干预策略”转化为一个优化问题如最优控制、动态规划、启发式算法。通过编程仿真对比不同策略下的指标表现验证模型和策略的有效性。接下来我会用一个假设性的、但极具代表性的题目场景——“区域应急物资配送系统优化”为例来详细拆解这四步如何落地。你会发现无论题目具体描述如何变化这套方法论的内核是相通的。1.1 从模糊描述到清晰定义问题抽象实战假设A题描述是这样的“某地区有多个应急物资储备库和受灾点需要考虑道路通行能力、车辆调度、物资需求动态变化等因素设计一套配送方案以在限定时间内满足需求并尽量降低总成本和社会影响。”这描述很笼统。我们的第一步工作就是把它“翻译”成数学建模语言。系统界定实体物资储备库设为S_i, i1..m、受灾点设为D_j, j1..n、配送车辆设为V_k, k1..p。属性储备库位置(x_i^s, y_i^s) 物资库存量Q_i(t)随时间变化。受灾点位置(x_j^d, y_j^d) 物资需求量R_j(t)随时间变化可能预测。车辆载重量C_k 行驶速度v_k 单位距离成本c_k。道路连接不同节点的边具有通行能力上限U_{ij}单位时间可通过的车辆数或物资量和实际通行时间T_{ij}(t)可能拥堵而时变。交互关系车辆从储备库装载物资沿道路网络行驶将物资卸载到受灾点。关键约束车辆载重限制任何时候车辆装载量 ≤C_k。库存限制从储备库i调出的物资总量 ≤Q_i(t)。需求约束运往受灾点j的物资总量应尽可能满足R_j(t) 允许部分满足但可能有惩罚。时间窗约束某些受灾点可能有最晚送达时间deadline_j。流量守恒车辆路径是连续的进出每个节点的车辆需平衡。经过这样的抽象一个模糊的“配送方案”问题就清晰化为一个带有时变需求和容量约束的多仓库、多车辆路径规划问题可能还融合了库存管理和流量分配。这就是数学建模的“翻译”艺术。注意题目不会直接给出所有参数如具体的T_{ij}(t)函数。我们需要根据常识或附加信息如“高峰时段通行能力下降”进行合理的假设并在论文中明确写出这些假设这是评分关键点。例如可以假设T_{ij}(t) base_time * (1 α * traffic_level(t)) 其中traffic_level(t)是随时间变化的拥堵系数。1.2 评估指标如何定义“更好”的方案题目要求“降低总成本和社会影响”这太定性。我们必须将其量化。一级指标总成本。 可以分解为运输成本 Σ (车辆行驶距离 * 单位距离成本)时间惩罚成本 Σ (对于超时送达的物资量 * 单位惩罚系数) 。 这体现了“社会影响”的一部分——延误带来的损失。未满足需求惩罚成本 Σ (未满足的物资需求量 * 单位短缺惩罚系数)。 这体现了另一个“社会影响”——需求未满足的后果。固定成本 使用的车辆数 * 单车辆固定成本如司机工资。总成本 运输成本 时间惩罚成本 未满足需求惩罚成本 固定成本。 各分项可以通过赋予权重来调整优先级。一级指标系统效率。 可以分解为需求满足率 (实际送达物资总量) / (总需求物资量)。平均送达时间 所有送达物资的运输时间平均值。车辆利用率 (总运输物资量) / (车辆总载重容量 * 使用时间)。 反映资源使用效率。公平性指数可选例如用基尼系数或泰尔指数衡量不同受灾点获得物资的及时性或充足度的差异。这直接量化“社会影响”中的公平性。构建这样一个多指标评价体系后我们的优化目标就可以明确了通常是在满足一定约束如时间窗、库存下最小化总成本或者在一个成本预算内最大化需求满足率和公平性。多目标问题可以通过加权求和、分层序列或帕累托前沿分析来处理。2. 模型构建从概念到方程有了清晰的定义和指标就可以着手建立数学模型了。针对上述应急物流场景一个强大且常用的框架是混合整数线性规划。2.1 核心模型基于时空网络的MILP模型我们将时间离散化为多个时段t 1, 2, ..., T。为每个车辆k、每个节点储备库或受灾点i、每个时段t定义决策变量x_{ijk}(t) 二进制变量车辆k在时段t是否从节点i行驶到节点j。f_{ijk}(t) 连续变量车辆k在时段t从节点i运往节点j的物资量。a_{ik}(t) 二进制变量车辆k在时段t是否位于节点i。l_{ik}(t) 连续变量车辆k在时段t离开节点i时的载货量。目标函数最小化总成本Minimize: Σ_t Σ_i Σ_j Σ_k [ distance_ij * cost_per_km_k * x_{ijk}(t) ] # 运输成本 Σ_j penalty_delay * max(0, actual_delivery_time_j - deadline_j) * delivered_amount_j # 时间惩罚 Σ_j penalty_shortage * shortage_amount_j # 短缺惩罚 Σ_k fixed_cost_k * (是否存在该车辆的路径) # 固定成本约束条件举例流量守恒车辆对于每个车辆k、每个非起点/终点的节点i、每个时段t 进入的车辆等于离开的车辆。Σ_j x_{jik}(t-τ_ji) Σ_j x_{ijk}(t) 对于所有 i, k, tτ_ji是从j到i的旅行时间时段数。载重约束车辆运量不能超过其容量。f_{ijk}(t) ≤ C_k * x_{ijk}(t) 对于所有 i, j, k, t库存约束从储备库i发出的所有物资不超过其库存。Σ_t Σ_j Σ_k f_{ijk}(t) ≤ Q_i(0) - Σ_t (内部消耗等) 对于所有 i需求约束运达受灾点j的物资量加上未满足量等于其需求。Σ_t Σ_i Σ_k f_{ijk}(t) shortage_j R_j_total 对于所有 j时间窗约束如果t deadline_j 则禁止向j配送物资或施加高惩罚。f_{ijk}(t) 0 for all i, k, and t deadline_j这个MILP模型非常精确但一旦节点数、车辆数、时段数增加变量和约束的数量会爆炸式增长求解会非常困难甚至不可行。这正是数学建模竞赛中需要权衡的地方模型的精确度 vs. 可求解性。2.2 简化与启发两阶段分层优化策略在实际竞赛中尤其是72小时内我们通常采用更巧妙的、可求解的近似方法。我推荐一个两阶段分层优化策略它平衡了效果和复杂度。第一阶段物资分配与流量规划宏观、静态目标不考虑具体的车辆路径只决定“从哪个仓库运多少物资到哪个受灾点”以最小化运输距离成本和短缺成本。模型这可以简化为一个运输问题或网络流问题。Minimize: Σ_i Σ_j (c_ij * y_ij) Σ_j (p_j * s_j) Subject to: Σ_j y_ij ≤ Supply_i (仓库供应量) Σ_i y_ij s_j ≥ Demand_j (受灾点需求s_j为短缺量) y_ij ≥ 0, s_j ≥ 0y_ij是从仓库i到受灾点j的物资量c_ij是单位运输成本可与距离成正比。求解这类线性规划问题规模小用MATLAB的linprog或Python的PuLP/SciPy可以快速求解。得到最优的y_ij*。第二阶段车辆路径规划微观、动态输入第一阶段得到的物资调拨量y_ij*。任务为每辆车安排具体的行驶路线和装卸货序列以执行这些调拨任务同时满足车辆容量、时间窗等约束。模型这本质上是一个带容量和时间窗的车辆路径问题。由于问题规模可能依然较大我们采用启发式算法。算法选择节约算法适合初始解生成原理是合并两条路径以节约总距离。模拟退火适合在节约算法的基础上进行路径优化通过随机扰动和概率接受劣解来跳出局部最优。遗传算法将路径编码为染色体通过选择、交叉、变异迭代进化。大规模邻域搜索破坏当前解的一部分如移除几个客户点然后以更优的方式重新插入搜索能力很强。实操心得在72小时竞赛中模拟退火SA是性价比最高的选择之一。它实现相对简单参数调节直观初始温度、降温系数、终止温度并且很容易与问题特性结合例如设计特定的“邻域”操作交换两个受灾点的访问顺序、将一段路径反转、将某个点移到另一条路径上。我通常会先用节约算法生成一个不错的初始解然后用SA进行精调。代码框架清晰易于调试和展示。3. 代码实现模块化与效率代码不是模型的简单翻译而是工程实现的艺术。结构混乱的代码会极大拖慢调试和验证的速度。我的建议是采用严格的模块化设计。3.1 数据结构设计首先设计好核心的数据结构这是所有操作的基础。# 使用Python示例 class Node: def __init__(self, node_id, node_type, x, y, supply0, demand0, deadlineNone): self.id node_id self.type node_type # depot or disaster self.x x self.y y self.supply supply # 对于仓库 self.demand demand # 对于受灾点可能是随时间变化的列表 self.deadline deadline class Vehicle: def __init__(self, vehicle_id, capacity, speed, cost_per_km): self.id vehicle_id self.capacity capacity self.speed speed self.cost_per_km cost_per_km self.route [] # 存储路径元素为 (node_id, arrival_time, deliver_amount) self.current_load 0 self.current_time 0 # 全局容器 nodes {} # node_id - Node object vehicles [] # list of Vehicle objects distance_matrix {} # (node_id_i, node_id_j) - distance3.2 第一阶段运输问题求解import pulp import numpy as np def solve_transportation_problem(supplies, demands, cost_matrix): 求解运输问题 :param supplies: 各仓库供应量列表 :param demands: 各受灾点需求量列表 :param cost_matrix: 成本矩阵cost_matrix[i][j] 从仓库i到受灾点j的单位成本 :return: 最优调拨量矩阵 allocation[i][j] num_suppliers len(supplies) num_demanders len(demands) # 定义问题 prob pulp.LpProblem(Emergency_Material_Allocation, pulp.LpMinimize) # 定义变量 x pulp.LpVariable.dicts(x, (range(num_suppliers), range(num_demanders)), lowBound0) shortage pulp.LpVariable.dicts(shortage, range(num_demanders), lowBound0) # 目标函数运输成本 短缺惩罚假设单位惩罚为高值如1000 prob pulp.lpSum([cost_matrix[i][j] * x[i][j] for i in range(num_suppliers) for j in range(num_demanders)]) \ 1000 * pulp.lpSum([shortage[j] for j in range(num_demanders)]) # 约束条件 for i in range(num_suppliers): prob pulp.lpSum([x[i][j] for j in range(num_demanders)]) supplies[i], fSupply_Constraint_{i} for j in range(num_demanders): prob pulp.lpSum([x[i][j] for i in range(num_suppliers)]) shortage[j] demands[j], fDemand_Constraint_{j} # 求解 prob.solve(pulp.PULP_CBC_CMD(msgFalse)) # 关闭求解器信息输出 # 或者使用 prob.solve(pulp.GUROBI()) 如果安装了Gurobi # 提取结果 allocation np.zeros((num_suppliers, num_demanders)) for i in range(num_suppliers): for j in range(num_demanders): allocation[i][j] pulp.value(x[i][j]) total_shortage sum(pulp.value(shortage[j]) for j in range(num_demanders)) print(f第一阶段求解完成。总短缺量: {total_shortage}) return allocation3.3 第二阶段模拟退火求解VRPTW这是代码的核心和难点。import math import random import copy def calculate_route_cost(vehicle, distance_matrix, service_time0.5): 计算单辆车的路径总成本距离成本时间惩罚 total_distance 0 total_time_penalty 0 current_time 0 current_load 0 penalty_per_hour 50 # 假设每小时延误惩罚50单位成本 route vehicle.route if not route: return 0, 0 # 从仓库出发 prev_node_id route[0][0] # 第一个节点的id for i in range(1, len(route)): cur_node_id, deadline, deliver_amount route[i] # 行驶距离和时间 travel_distance distance_matrix[(prev_node_id, cur_node_id)] travel_time travel_distance / vehicle.speed current_time travel_time # 服务时间卸货 current_time service_time current_load - deliver_amount # 计算时间惩罚 if deadline is not None and current_time deadline: total_time_penalty (current_time - deadline) * penalty_per_hour * deliver_amount total_distance travel_distance prev_node_id cur_node_id distance_cost total_distance * vehicle.cost_per_km return distance_cost, total_time_penalty def initial_solution_by_savings(allocation, nodes, vehicles, distance_matrix): 使用节约算法构造初始解 # 这是一个简化版的节约算法实现框架 # 1. 为每个受灾点创建一条从最近仓库出发再返回的单独路线 # 2. 计算所有可能路线合并的“节约值”S_ij d_i0 d_0j - d_ij (0代表仓库) # 3. 按节约值从大到小排序尝试合并路线如果满足车辆容量和时间约束则合并。 # 4. 重复步骤3直到无法合并。 # 由于实现较长此处给出核心逻辑框架具体实现需根据时间窗等约束调整。 print(正在使用节约算法生成初始路径...) # ... (具体实现代码) return vehicles_with_routes def simulated_annealing_vrp(vehicles, distance_matrix, nodes, allocation, iterations5000, temp1000, cooling_rate0.995): 模拟退火算法优化车辆路径 best_vehicles copy.deepcopy(vehicles) current_vehicles copy.deepcopy(vehicles) # 计算当前成本 def total_cost(veh_list): cost 0 for v in veh_list: dist_cost, time_penalty calculate_route_cost(v, distance_matrix) cost dist_cost time_penalty # 可以加上固定成本cost len(veh_list) * fixed_cost return cost current_cost total_cost(current_vehicles) best_cost current_cost for it in range(iterations): # 1. 产生邻域解这里采用“2-opt*”交换作为示例 new_vehicles copy.deepcopy(current_vehicles) # 随机选择两条不同的车辆路线 v1_idx, v2_idx random.sample(range(len(new_vehicles)), 2) route1 new_vehicles[v1_idx].route route2 new_vehicles[v2_idx].route if len(route1) 1 and len(route2) 1: # 随机选择交换点不包含仓库点 i random.randint(1, len(route1)-1) j random.randint(1, len(route2)-1) # 执行交换将route1从i之后的部分与route2从j之后的部分互换 new_route1 route1[:i] route2[j:] new_route2 route2[:j] route1[i:] # 检查新路线是否满足容量约束简单检查实际需更严谨 if check_capacity(new_vehicles[v1_idx], new_route1) and check_capacity(new_vehicles[v2_idx], new_route2): new_vehicles[v1_idx].route new_route1 new_vehicles[v2_idx].route new_route2 # 2. 计算新解成本 new_cost total_cost(new_vehicles) # 3. 判断是否接受新解 delta_cost new_cost - current_cost if delta_cost 0 or random.random() math.exp(-delta_cost / temp): current_vehicles new_vehicles current_cost new_cost if current_cost best_cost: best_vehicles copy.deepcopy(current_vehicles) best_cost current_cost print(fIteration {it}: New best cost found: {best_cost:.2f}) # 4. 降温 temp * cooling_rate # 可选增加一些针对时间窗的局部搜索操作如节点在路径内重排、节点插入等 print(f模拟退火优化完成。最终最优成本: {best_cost:.2f}) return best_vehicles, best_cost def check_capacity(vehicle, new_route): 检查新路径是否满足车辆容量约束 total_deliver sum([amount for _, _, amount in new_route]) return total_deliver vehicle.capacity3.4 可视化与结果输出结果可视化至关重要能直观展示方案优劣。import matplotlib.pyplot as plt def visualize_solution(nodes, vehicles, allocation): 可视化最终的配送路径 plt.figure(figsize(12, 8)) # 绘制节点 for node_id, node in nodes.items(): if node.type depot: plt.plot(node.x, node.y, ks, markersize15, labelDepot if node_id list(nodes.keys())[0] else ) plt.text(node.x, node.y0.2, fD{node_id}, fontsize12, hacenter) else: plt.plot(node.x, node.y, ro, markersize10, labelDisaster Site if node_id list(nodes.keys())[-1] else ) plt.text(node.x, node.y0.2, fS{node_id}, fontsize10, hacenter) # 绘制路径 colors [b, g, m, c, y] for idx, vehicle in enumerate(vehicles): if vehicle.route: color colors[idx % len(colors)] path_x [] path_y [] # 起点假设第一个仓库 depot_id vehicle.route[0][0] path_x.append(nodes[depot_id].x) path_y.append(nodes[depot_id].y) for node_id, _, _ in vehicle.route[1:]: # 跳过第一个仓库点起点 path_x.append(nodes[node_id].x) path_y.append(nodes[node_id].y) # 回到仓库假设最后回到起点仓库 path_x.append(nodes[depot_id].x) path_y.append(nodes[depot_id].y) plt.plot(path_x, path_y, color-o, linewidth2, markersize5, labelfVehicle {vehicle.id} if idx 5 else ) plt.xlabel(X Coordinate) plt.ylabel(Y Coordinate) plt.title(Optimized Emergency Material Delivery Routes) plt.grid(True, linestyle--, alpha0.7) plt.legend(locbest) plt.axis(equal) plt.show() # 输出关键指标表格 print(\n *60) print(配送方案绩效汇总) print(*60) total_distance 0 total_time_penalty 0 total_delivered 0 total_demand sum([n.demand for n in nodes.values() if n.typedisaster]) for v in vehicles: if v.route: dist_cost, time_penalty calculate_route_cost(v, distance_matrix) delivered sum([amount for _, _, amount in v.route]) total_distance dist_cost / v.cost_per_km # 换算回距离 total_time_penalty time_penalty total_delivered delivered print(f车辆 {v.id}: 路径节点数 {len(v.route)}, 配送量 {delivered:.1f}, 距离成本 {dist_cost:.1f}, 时间惩罚 {time_penalty:.1f}) print(-*60) print(f总计: 配送量 {total_delivered:.1f}/{total_demand:.1f} (满足率 {total_delivered/total_demand*100:.1f}%)) print(f 总行驶距离: {total_distance:.1f}) print(f 总时间惩罚成本: {total_time_penalty:.1f}) print(f 总成本距离时间: {total_distance*vehicles[0].cost_per_km total_time_penalty:.1f}) print(*60)4. 竞赛实战时间管理与论文写作有了思路和代码框架如何在72小时内高效产出才是决胜关键。4.1 三天时间轴规划第一天上午-中午彻底吃透题目完成问题分析。全员精读题目2-3遍划出所有名词、条件、目标。集体讨论确定系统边界、核心变量、关键约束。画出系统示意图。确定初步的建模方向和技术路线用哪种模型什么算法。这个阶段宁可慢也要达成共识避免中途返工。第一天下午-晚上完成模型建立与第一版求解。根据技术路线分工撰写模型的数学公式LaTeX。负责编程的同学开始搭建代码框架实现基础的数据读入、距离计算、简单贪婪算法生成初始解。晚上睡前必须有一个能跑通的、输出基础结果的程序哪怕结果很差。这是进度的锚点。第二天全天模型优化、算法调试与结果分析。编程同学集中精力优化算法如调试模拟退火的参数、改进邻域操作。其他同学开始撰写论文的“问题重述”、“模型假设”、“符号说明”和“模型建立”部分。下午进行第一次完整的模型运行得到一组可分析的结果。分析结果是否合理哪些指标不好是模型缺陷还是参数问题据此调整模型或算法。开始绘制核心的图表如路径图、指标对比图。第三天全天-通宵论文撰写、完善与收尾。上午完成论文核心部分模型求解、结果分析的撰写。所有图表插入到位。下午撰写“灵敏度分析”改变关键参数看结果如何变化和“模型评价与推广”优点、缺点、可改进处、其他应用场景。晚上撰写摘要、关键词、参考文献。摘要最重要需精炼包含问题、方法、模型、算法、主要结果和结论。反复修改。深夜-凌晨全文交叉检查。检查公式编号、图表引用、错别字、语法。统一格式。最终生成PDF。4.2 论文写作核心要点论文是你们工作的唯一呈现务必重视。摘要单独一页。用第三人称写。结构1-2句问题背景 → 本文工作概述针对什么问题建立了什么模型采用了什么方法→ 分点简述主要模型/方法如“首先…其次…然后…”→ 给出最重要的量化结果“最终实现了XX%的需求满足率将总成本降低了XX”→ 简要结论模型特点、优势。模型假设合理且必要。例如“假设各路段通行速度恒定”、“假设物资需求在决策期内保持不变”、“忽略车辆装卸货时间”。每一条假设都要服务于简化模型且需讨论其合理性。符号说明用三线表清晰列出所有主要变量、符号、含义及单位。模型建立分小节论述。先文字描述思路再给出严谨的数学公式。公式要编号并在后文引用。模型求解详细说明所使用的算法。如果是经典算法如模拟退火简述原理后重点说明你如何将其应用于本题编码方式、邻域设计、适应度函数、参数设置。给出算法流程图。结果分析用图表说话。路径图、成本随迭代下降图、不同方案对比表。对结果进行解释而不是简单罗列数据。例如“如图3所示我们的方案避免了车辆绕远路形成了几个高效的配送闭环。”灵敏度分析体现模型的稳健性。例如改变车辆数量、改变需求波动幅度、改变时间窗严格程度观察关键指标总成本、满足率的变化趋势并分析原因。模型评价客观公正。写出模型的3-4个优点如考虑因素全面、求解效率高以及1-2个缺点或可改进之处如未考虑道路动态拥堵、假设需求预测完全准确可能过于理想。4.3 常见陷阱与避坑指南追求完美模型无法求解这是最常见错误。记住竞赛的黄金法则“一个能求解的近似模型远胜于一个无法求解的精确模型。” 优先保证模型在时限内能出结果。代码调试黑洞不要一次性写几百行再调试。采用增量开发写一个函数测试一个函数。多用print语句输出中间变量值。为关键函数编写简单的单元测试。论文与代码脱节论文中描述的模型和算法必须与最终提交的代码核心逻辑一致。评委可能会抽查代码。避免论文写的是A算法代码实现的是B算法。忽视可视化一张清晰的路径图、一张趋势变化折线图比大段文字描述有力得多。利用好matplotlib或seaborn。摘要写成引言摘要里不要写“我们查阅了文献”、“我们团队经过讨论”直接上干货做了什么怎么做结果如何。最后时刻大改第三天下午之后严禁对模型或算法进行结构性大改。此时只做微调、参数优化和论文润色。大改极易导致崩溃无法完赛。不备份必须使用Git或定时将代码、论文备份到云端如GitHub, Gitee。防止电脑故障导致前功尽弃。最后保持沟通合理休息。72小时是马拉松不是冲刺。每隔几个小时团队同步一下进度。第二天晚上务必睡上4-5个小时清晰的头脑在最后一天至关重要。数学建模竞赛比拼的不仅是技术更是团队协作、时间管理和在压力下解决问题的能力。把这套思路和代码框架作为你的“瑞士军刀”根据具体题目灵活变通你就能构建出结构清晰、逻辑严谨、求解有效的方案在长三角乃至更高级别的竞赛中脱颖而出。