1. 从一道赛题看旅游路线设计的数学内核
如果你参加过数学建模竞赛,或者对运筹优化感兴趣,那么“最佳旅游路线设计”这个题目一定不陌生。它听起来像是一个旅游攻略问题,但其内核是一个经典的组合优化难题——旅行商问题及其变种。Mathorcup第四届的这道C题,正是将TSP问题置于一个更贴近现实的场景中,要求参赛者不仅要找到最短路径,还要综合考虑时间、成本、景点评分等多重约束,最终设计出“最佳”而非“最短”的路线。这其中的“最佳”,就是数学建模的魅力所在,也是这道题区分于教科书例题的关键。今天,我就以这道赛题为引子,抛开竞赛的紧张氛围,和大家深入聊聊,当我们用数学工具去规划一次旅行时,到底在解决哪些问题,以及如何用LINGO这样的优化软件将想法落地。无论你是数学建模的新手,还是对优化算法感兴趣的开发者,相信这篇从实战出发的拆解都能给你带来一些新的启发。
这道题通常会给出一组城市或景点,以及它们之间的交通距离(或时间、费用),同时每个景点可能有各自的游览时间、门票费用、满意度评分等属性。参赛者的任务是在满足总时间或总预算限制的前提下,规划一条从起点出发、游览所有或部分指定景点、最后返回起点的环路,使得某个目标(如总满意度最高、总成本最低、或综合效用最大)达到最优。这立刻将问题从单纯的“最短路径”提升到了“多目标决策”的层面。我们面对的不再是一个静态的网络,而是一个充满权衡的动态系统。选择绕远路去一个高分景点是否值得?在有限的时间里,是追求广度(去更多景点)还是深度(在少数精品景点花费更多时间)?这些旅行中真实存在的纠结,正是数学模型需要量化并求解的核心。
2. 问题拆解:从旅行需求到数学模型框架
面对这样一个多因素交织的问题,直接上手编程或调包是行不通的。第一步,也是最重要的一步,是进行严谨的问题分析和模型构建。我们需要把模糊的“最佳”转化为数学语言中清晰的“目标函数”和“约束条件”。
2.1 定义核心要素与决策变量
首先,要明确问题的基本要素。假设我们有N个需要决策是否游览的景点(节点),编号为1, 2, ..., N。通常,节点1被设定为起点和终点(例如酒店所在地)。我们需要定义最关键的决策变量。最常用的是0-1决策变量 x_{ij}:如果路线中包含从景点i直接前往景点j的路径,则 x_{ij} = 1,否则为 0。同时,为了消除子回路(即形成多个不包含起点的小环路),往往需要引入辅助变量 u_i,它可以理解为景点i在路线中的访问次序。
接下来是参数,也就是题目给出的已知数据:
- 距离矩阵 D:
D[i][j]表示从景点i到景点j的交通距离(或时间、成本)。 - 景点属性:游览时间
t_i,门票费用c_i,满意度评分s_i。 - 全局约束:总可用时间
T_max,总预算B_max。
2.2 构建目标函数:什么是“最佳”?
“最佳”是一个主观词,在模型中必须客观化。常见的目标函数有以下几种,有时也需要组合使用:
- 最小化总成本:这是最直观的目标之一。总成本通常包括交通成本和景点门票成本。目标函数可以写为:Minimize Z = Σ_i Σ_j (D[i][j] * x_{ij}) + Σ_i (c_i * y_i),其中 y_i 是0-1变量,表示是否访问景点i。
- 最大化总满意度:如果景点评分代表满意度,那么目标就是最大化所游览景点的总评分:Maximize Z = Σ_i (s_i * y_i)。
- 最大化性价比(单位成本的满意度):这是一个比率目标,处理起来更复杂,有时可以转化为在成本约束下最大化满意度。
- 多目标优化:例如,同时希望“满意度尽量高”且“成本尽量低”。这类问题通常没有唯一的最优解,而是一组“帕累托最优”解。竞赛中常用的处理方法是线性加权法,将多目标转化为单目标。例如,定义综合效用 U = α * (总满意度) - β * (总成本),其中α和β是权重系数,反映了决策者对满意度和成本的重视程度。权重的设定需要结合题目背景或通过灵敏度分析来讨论。
在Mathorcup这道题中,很可能需要采用第三种或第四种思路,即综合考虑花费和体验,这也是实际旅行规划的常态。
2.3 确立约束条件:现实的枷锁
目标函数定义了方向,约束条件则划定了可行的范围。除了经典的TSP约束(每个景点只能离开一次、到达一次、消除子回路),还需要加入资源限制:
- 流量平衡约束:对于每个景点i,进入的路径数等于离开的路径数。Σ_j x_{ji} = Σ_j x_{ij} = y_i (对于起点,可能等于1或2,取决于模型设定)。
- 起点终点约束:通常要求从节点1出发并最终回到节点1。
- 子回路消除约束:这是TSP建模的难点。最常用的是MTZ约束:u_i - u_j + N * x_{ij} ≤ N-1, 对于所有 i, j ≥ 2 且 i ≠ j。这个约束保证了路径的连续性,不会形成多个圈。
- 时间约束:总时间 = 交通时间 + 游览时间 ≤ T_max。即 Σ_i Σ_j (D[i][j] * x_{ij]) + Σ_i (t_i * y_i) ≤ T_max。
- 预算约束:总费用 = 交通费 + 门票费 ≤ B_max。即 Σ_i Σ_j (D[i][j] * x_{ij]) + Σ_i (c_i * y_i) ≤ B_max。
- 可选景点约束:如果景点不是必须全部访问,那么还需要决策 y_i。并且,访问一个景点(y_i=1)的前提是有一条路径进入它(Σ_j x_{ji} = 1)。
将以上所有要素整合起来,我们就得到了一个完整的混合整数线性规划模型。它可能看起来复杂,但结构清晰,为后续的求解奠定了坚实的基础。
3. LINGO求解实战:将模型转化为代码
模型建立后,下一步就是求解。对于中小规模的问题(节点数在20-30左右),使用专业的优化软件LINGO是非常高效的选择。LINGO的语法接近数学表达,可以直观地描述模型。下面,我将基于一个简化场景,展示如何将上述模型转化为LINGO代码,并附上关键环节的解读。
假设我们有5个景点(节点1为酒店),数据如下:
- 距离矩阵D(单位:小时,假设速度恒定,故距离即时间):
节点: 1 2 3 4 5 1: 0 2 9 3 6 2: 2 0 7 4 8 3: 9 7 0 5 2 4: 3 4 5 0 1 5: 6 8 2 1 0 - 景点属性:
节点 游览时间t_i(h) 门票费c_i(元) 评分s_i 2 3 100 8 3 2 150 9 4 1.5 80 7 5 2.5 120 6 - 全局限制:总时间T_max=12小时,总预算B_max=400元。
- 目标:最大化综合效用 U = 总评分 - 0.01 * 总成本(这里权重系数将成本单位“元”的影响缩小,与评分无量纲量匹配)。
对应的LINGO程序代码如下:
MODEL: ! 定义集合; SETS: city /1..5/: u, t, c, s, y; ! city: 景点集合,u为MTZ辅助变量,t游览时间,c门票费,s评分,y是否访问; link(city, city): d, x; ! link: 城市间的连接,d为距离/交通时间,x为0-1决策变量; ENDSETS ! 输入数据; DATA: d = 0, 2, 9, 3, 6, 2, 0, 7, 4, 8, 9, 7, 0, 5, 2, 3, 4, 5, 0, 1, 6, 8, 2, 1, 0; t = 0, 3, 2, 1.5, 2.5; ! 节点1(酒店)游览时间为0; c = 0, 100, 150, 80, 120; s = 0, 8, 9, 7, 6; T_max = 12; B_max = 400; ENDDATA ! 定义目标函数:最大化综合效用; MAX = @SUM(city(i): s(i)*y(i)) - 0.01 * ( @SUM(link(i,j): d(i,j)*x(i,j)) + @SUM(city(i): c(i)*y(i)) ); ! 约束条件部分; ! 1. 流量平衡约束:对于每个城市i,如果被访问,则进入和离开各一次; @FOR(city(i): @SUM(city(j) | j #NE# i: x(j,i)) = y(i); @SUM(city(j) | j #NE# i: x(i,j)) = y(i); ); ! 2. 必须从节点1出发并回到节点1,且必须访问节点1(作为起点/终点); y(1) = 1; @SUM(city(j) | j #GT# 1: x(1,j)) = 1; @SUM(city(j) | j #GT# 1: x(j,1)) = 1; ! 3. 子回路消除约束(MTZ约束); @FOR(link(i,j) | i #GT# 1 #AND# j #GT# 1 #AND# i #NE# j: u(i) - u(j) + 5 * x(i,j) <= 4; ); ! 设置u变量的边界,对于非起点城市; @FOR(city(i) | i #GT# 1: u(i) >= 2; u(i) <= 5; ); ! 4. 时间约束:交通时间 + 游览时间 <= T_max; @SUM(link(i,j): d(i,j)*x(i,j)) + @SUM(city(i): t(i)*y(i)) <= T_max; ! 5. 预算约束:交通费(此处假设单位距离费用为1) + 门票费 <= B_max; ! 注意:此处d(i,j)同时代表距离和时间,假设费用与距离成正比,系数为1。 若题目单独给出交通费用矩阵,需替换d(i,j)为对应费用。 @SUM(link(i,j): d(i,j)*x(i,j)) + @SUM(city(i): c(i)*y(i)) <= B_max; ! 本例为简化,使用与时间相同的矩阵代表费用; @SUM(link(i,j): d(i,j)*x(i,j)) + @SUM(city(i): c(i)*y(i)) <= B_max; ! 6. 定义变量类型; @FOR(link(i,j): @BIN(x(i,j))); ! x(i,j)为0-1变量; @FOR(city(i): @BIN(y(i))); ! y(i)为0-1变量; @FOR(city(i): @GIN(u(i))); ! u(i)为一般整数变量(MTZ约束需要); END代码关键点解读与实操心得:
- 集合定义是根基:LINGO基于集合编程,
SETS部分定义了city和link两个集合,并声明了附着其上的属性(变量和参数)。这种定义方式使得后续的约束可以简洁地用@FOR和@SUM对整个集合进行操作,避免了冗长的循环语句,是LINGO高效性的体现。 - MTZ约束的细节:子回路消除约束
u(i) - u(j) + N * x(i,j) <= N-1是核心。其中N是城市总数(本例为5)。这个约束确保了如果x(i,j)=1(即走了i到j的路径),则必须有u(j) >= u(i) + 1,从而给每个访问的城市赋予一个递增的序号,防止形成回路。约束条件中的| i #GT# 1 #AND# j #GT# 1 #AND# i #NE# j表示该约束仅对非起点(i>1且j>1)且i不等于j的节点对生效,这是标准的写法,可以避免不必要的约束和起点处理冲突。 - 起点处理的技巧:我们强制
y(1)=1,并约束从节点1出发和进入的路径各为1条。对于u变量,起点通常不参与MTZ约束,而非起点城市的u值被限定在[2, N]之间,这有助于求解器更快地找到可行解。 - 目标函数权重的设定:本例中,目标函数是
总评分 - 0.01 * 总成本。为什么是0.01?这是一个尺度统一的过程。评分s_i的量级是个位数(6-9),而总成本(交通+门票)的量级是数百。如果不加调整,成本项将完全主导目标函数,导致模型退化为纯粹的成本最小化。乘以0.01(或除以100)相当于将成本单位从“元”转换为“百分之一元”,使其数值量与评分接近,从而让两个目标都能对结果产生影响。在实际竞赛中,这个权重的选取需要说明理由,或者进行灵敏度分析,展示不同权重下最优解的变化。 - 求解与结果查看:在LINGO中编写完上述代码后,点击“Solve”按钮。求解完成后,在“Solution Report”窗口中可以查看最优目标函数值,以及所有变量的值。重点关注
x(i,j)和y(i)的取值。例如,x(1,2)=1表示最优路线中从1(酒店)直接去了2号景点。根据这些为1的x变量,就能拼接出完整的旅行路线。
4. 模型对比与拓展:不同场景下的建模思路
上述模型是一个基础而完整的框架。但在实际竞赛或应用中,题目条件会变化,需要我们调整模型。这里对比几种常见变体,并讨论其建模思路。
4.1 经典TSP vs. 带约束的TSP(本题基础)
- 经典TSP:目标唯一,即最小化总旅行距离。约束只有每个城市访问一次、形成单个回路。它不考虑时间、预算、景点选择。求解方法关注于算法效率(如动态规划、启发式算法)。
- 带约束的TSP(本题):在TSP基础上增加了资源约束(时间、预算)和节点选择变量(y_i)。目标可能变为综合效用最大化。建模核心在于引入0-1决策变量y_i来灵活选择景点,并将资源消耗表示为
Σ d*x + Σ t*y的形式。求解时,整数规划的特征更明显,依赖于LINGO、CPLEX等求解器的分支定界/割平面能力。
4.2 团队旅行与多车辆路径问题(VRP)
如果题目涉及一个团队分乘多辆车游览,问题就演变为车辆路径问题。核心变化是:
- 决策变量:需要引入表示车辆k是否从i行驶到j的变量 x_{ijk}。
- 约束:每辆车都有容量(载客量)约束,每条路线都必须从共同的起点(车场)出发并返回。
- 目标:可能是最小化总行驶距离,或最小化使用车辆数,或均衡各车路线时长。
- 建模复杂度:急剧上升。通常需要更强的子回路消除约束(如流平衡约束的扩展),并且求解难度更大,对于稍大规模的问题,往往需要设计启发式算法(如节约算法、扫描算法)来获得满意解。
4.3 动态与随机情境下的考量
更进一步的挑战是引入不确定性,例如:
- 交通时间随机:两点间的行驶时间不是一个定值d_{ij},而是一个随机变量(如服从某种分布)。此时,严格的时间约束
Σ (时间)*x <= T_max可能变得不现实。 - 建模思路:可以采用鲁棒优化或随机规划。例如,将时间约束改为概率约束:P(总时间 ≤ T_max) ≥ 0.95,即要求总时间不超过T_max的概率至少为95%。这需要知道时间随机变量的分布,并将该概率约束转化为确定的等价类进行求解,难度较高,常出现在研究生阶段的赛题中。
- 景点开放时间窗:每个景点只能在特定时间段内访问(如9:00-17:00)。这需要引入时间累计变量,并添加复杂的时序约束,确保到达每个景点的时间在其时间窗内。这类问题称为带时间窗的车辆路径问题,是运筹学的研究热点。
5. 竞赛实现中的常见“坑”与应对策略
即便模型建得再漂亮,代码写得再规范,在实际求解和结果分析中还是会遇到各种问题。结合我多次参赛和辅导的经验,以下几个“坑”值得特别注意。
5.1 求解规模与时间瓶颈
LINGO在求解整数规划时,采用分支定界法。问题规模(节点数N)稍大,求解时间就会指数级增长。对于N>30的问题,很可能在规定赛时内无法得到最优解。
- 应对策略:
- 初始解启发:给LINGO提供一个好的初始解可以大幅加速求解。例如,先用最近邻法、插入法等简单启发式算法快速生成一条可行路线,然后将这条路线对应的x_{ij}和y_i的值作为初始值赋给LINGO的变量。在LINGO中,可以用
@FOR循环和INIT部分来实现。 - 松弛与分解:如果问题可以分解为几个相对独立的子问题(例如,将景点按区域聚类),可以先分别求解子问题,再考虑连接。
- 设置求解器参数:在LINGO的
Options菜单中,可以调整整数求解器的相关参数,如“最优性容差”,适当放宽可以加快求解速度,但会损失一点解的质量。 - 转向启发式算法:对于大规模问题,应果断放弃求精确最优解,在论文中明确说明,并采用模拟退火、遗传算法、蚁群算法等元启发式算法寻找高质量近似解。这往往是更现实的竞赛策略。
- 初始解启发:给LINGO提供一个好的初始解可以大幅加速求解。例如,先用最近邻法、插入法等简单启发式算法快速生成一条可行路线,然后将这条路线对应的x_{ij}和y_i的值作为初始值赋给LINGO的变量。在LINGO中,可以用
5.2 模型无可行解:检查约束冲突
运行LINGO后,有时会直接报告“No feasible solution found”。这意味着在给定的约束条件下,不存在任何一条路线能满足所有要求。
- 排查步骤:
- 检查数据输入:首先核对所有参数(D, t, c, T_max, B_max)是否输入正确,单位是否一致。一个错误的数据可能导致约束过紧。
- 放松约束:逐步放松约束,找到“卡脖子”的环节。例如,先注释掉预算约束,看是否有解;再注释掉时间约束。如果去掉某个约束后模型有解,说明该约束可能过于严格。
- 分析约束紧度:计算一个“理论下限”。例如,即使马不停蹄地跑,访问所有景点的最小总时间 = 最短哈密顿回路的交通时间 + 所有景点的游览时间。如果这个值已经大于T_max,那么问题显然无解。这时就需要修改问题本身,比如允许不访问某些景点(y_i变量正是为此设计),或者向评委说明,在当前资源下无法完成全部游览,并给出一个访问最多景点的方案(此时目标函数需调整)。
5.3 结果分析与灵敏度检验
得到最优解后,工作只完成了一半。深入的结果分析能为论文增色不少。
- 路线可视化:用MATLAB、Python的Matplotlib或在线工具将最优路线画在地图上,一目了然。图中可以标注景点编号、路径方向、各段距离/时间。
- 资源利用分析:计算最优解下的总时间利用率和预算利用率。例如,总用时11.5小时/12小时=95.8%。这能说明你的方案对资源的利用是否充分。
- 灵敏度分析:这是体现建模深度的关键。研究关键参数变动对最优解的影响。
- 权重灵敏度:改变目标函数中评分与成本的权重(如前文中的0.01),观察最优路线和景点选择如何变化。可以绘制帕累托前沿图,展示不同权重下的“最佳”权衡。
- 资源灵敏度:分析总时间T_max或总预算B_max增加或减少10%会对最优目标值产生多大影响。这能回答“如果预算增加100元,我们的综合体验能提升多少?”这类实际问题。
- 参数灵敏度:如果某个景点的评分s_i或时间t_i存在不确定性,可以分析其变化在什么范围内,当前的最优路线结构(即哪些x_{ij}=1)保持不变。这称为“最优基不变”的灵敏度分析,在线性规划中可以直接从单纯形法的最终表中读取,对于整数规划则需要做参数规划。
5.4 论文写作中的表达要点
数学建模竞赛评阅时,模型和求解固然重要,但清晰的表达同样关键。
- 避免“黑箱”描述:不要只写“我们用LINGO求解”,而要详细说明你建立的模型具体是什么(列出目标函数和所有约束的数学公式),以及你是如何将这个模型“翻译”成LINGO代码的(可以像本文第三节那样,对关键代码段进行解释)。
- 突出创新与调整:如果对经典模型做了改进(例如设计了新的子回路消除约束、提出了分段加权目标函数),一定要重点说明动机和优势。
- 结果呈现要直观:除了文字,多用表格和图表。例如,用一张表清晰列出最优路线的访问顺序、各段耗时、各景点花费;用另一张表展示灵敏度分析的结果。
- 讨论模型的优缺点:没有完美的模型。在论文结尾,客观地讨论你所建模型的局限性(例如,假设交通时间是固定的,未考虑拥堵;假设满意度是线性可加的,未考虑边际效应递减等),并提出可能的改进方向,这能展示你的批判性思维。
规划一条旅行路线,从数学上看,是在一个充满约束的网络中寻找最优路径。Mathorcup的这道题,巧妙地将经典的运筹学问题包装在一个生活化的场景里。通过它,我们实践了从问题分析、模型构建、到软件求解、结果分析的全过程。其中,如何定义“最佳”,如何用0-1变量表达选择,如何用MTZ约束破除子回路,以及如何用LINGO实现求解,是贯穿始终的技术主线。更重要的是,我们看到了数学模型如何将主观的“体验”和客观的“成本”统一到一个框架下进行量化权衡。在实际操作中,我最大的体会是:数据的尺度预处理和约束的可行性检验,是保证模型能顺利求解的两个基石。前者决定了目标函数是否真正反映了你的意图,后者避免了在错误的方向上浪费时间。下次当你再规划旅行时,或许可以下意识地想想,这背后的优化问题,是不是也有一个优雅的数学解呢?