, {/ T3 |6 i/ F2 B6 k
+ h2 y T- p' L; _+ o: C# T2 M5 w
& N2 _9 A* p/ z# u, \2 ~1 `
( w- P# [% z6 q7 Z" X: ~
, b9 c/ T% x R* e, s
. O4 j5 _6 x) [3 F
. k1 H1 s- N" B% o* K* d; S
# ?; O8 \+ }1 q+ W! g( n6 I
- from itertools import permutations
4 _& b* I8 x+ g( b i: Q$ d. W - import math9 o1 Q( n- R/ B; c+ y
- 1 \, v, n! M( Z6 l
- def tsp_brute(dist):
2 N2 t/ o0 }& M2 B - """dist: n×n 距离矩阵,返回 (最短长度, 最优回路)"""& r6 l7 I& Q1 r* k' I0 A
- n = len(dist)8 {. u+ h2 o Z
- best_len, best_tour = math.inf, None5 X' N: Z) d' r1 q
- for perm in permutations(range(1, n)): # 固定起点 0/ u# [; W. d; K# ^
- tour = [0] + list(perm)# H: p4 w- W% D9 b
- L = sum(dist[tour[i]][tour[i+1]] for i in range(n-1)) + dist[tour[-1]][0]
0 H; F; g) P6 z\" K - if L < best_len:: Q m/ Q2 F7 m\" g1 \6 B1 _
- best_len, best_tour = L, tour' g2 X& ^: A7 Y4 k; m
- return best_len, best_tour
0 w- v* A2 c Y! K
$ G. c& ~( s3 [# E/ m- # ---------- 2-opt 局部搜索(中大规模) ----------
$ q1 F3 l. s- ]& P) } - def tsp_2opt(dist, max_iter=1000):: C. U) H( `3 B8 A8 R1 ~4 {
- n = len(dist)5 w3 d5 I2 Q5 { _
- tour = list(range(n)) # 初始:自然顺序
: q; q3 F( J6 h; B$ ?$ v3 ~ - improved = True: |. s. x8 j# w+ Z5 i; ^% f/ s
- while improved and max_iter > 0:
' e* }4 d' N v- _ - improved = False
% Z4 V# V. ?7 `+ F% H\" b5 o4 j: [ - for i in range(1, n-2):: k0 E2 B9 F# l
- for k in range(i+1, n):
\" ?7 c) r1 |9 m( T8 m - # 翻转 tour[i:k+1]
$ ` |2 \. X1 [* w3 b5 H\" P: d - new_tour = tour[:i] + tour[i:k+1][::-1] + tour[k+1:]. B% C5 O8 P5 z' B# R- @, m, r
- cur = sum(dist[tour[j]][tour[j+1]] for j in range(n-1)) + dist[tour[-1]][tour[0]]
: E1 s; n |0 ^0 z8 h - new = sum(dist[new_tour[j]][new_tour[j+1]] for j in range(n-1)) + dist[new_tour[-1]][new_tour[0]]
( @* k( @( q\" L$ I4 s3 ]: y - if new < cur:
; W* h; _: v. _: z6 [\" u, @9 } - tour, improved = new_tour, True6 ?) K\" B7 U$ M* s0 W; `' }
- max_iter -= 1! U7 ^4 |4 D! d
- L = sum(dist[tour[j]][tour[j+1]] for j in range(n-1)) + dist[tour[-1]][tour[0]]* `5 y- t7 c1 E7 l* P
- return L, tour9 y; c* {7 b% r. R* V\" L% b
- * T8 f/ {4 A8 K) }
- # ---------- 测试:4 城市 ----------
% P1 |5 G! X3 w7 \0 r& | - dist = [. v( E5 ] p6 D7 d# l
- [0, 10, 15, 20],6 Z' P: p) d! B9 w
- [10, 0, 35, 25],
0 s# k7 [% [; O) W6 z - [15, 35, 0, 30],
# g& `' K% ], \* n' @ - [20, 25, 30, 0]
& O! a, ~\" ~' u% d& E9 Z: t# H- ` - ]% F# h* F+ Z* j8 p9 q# S H9 ?
- print("暴力 DFS:", tsp_brute(dist)) # 最短长度 80,回路 0→1→3→2→06 m6 f2 Q& H5 f: T! h8 b( _9 w' k
- print("2-opt :", tsp_2opt(dist))
复制代码 7.2 运行结果- 暴力 DFS: (80, [0, 1, 3, 2])& K* a7 y8 K\" s6 J7 H5 [
- 2-opt : (95, [0, 1, 2, 3])
复制代码观察:暴力 DFS 保证全局最优( 时可行)。2-opt 从自然顺序出发卡在局部最优 95,未能到达最优解 80——这正是 2-opt 的经典陷阱:单次运行可能陷入局部最优。竞赛中应多次随机重启或结合其他启发式(如先用最近邻初始化再 2-opt)。这也是为什么论文里必须报告"与精确解的 gap",而不是只报一个数字。 8. 竞赛真题映射年份 CUMCM 题目方向 TSP 变体
# O \. m# A% \/ p. F( _' P1998B灾情巡视路线巡检 TSP(带时间窗 / 部分覆盖)9 p H* X0 B2 b/ a. j& I( k
2001C公交车线路设计路径规划 + 容量约束(VRP 变体)
) F& n% e D4 d" R% }& J- D2008B啤酒运输问题配送路径优化% |; _0 L% N" f; O& o; L
2011A高速公路收费站路网遍历 + 费用优化
# f- {" r5 |1 [2015A太阳能小屋设备布点 + 巡检路径
9 T/ z/ ?) @! z4 N7 }* ~- w/ Q2017B煤炭产量预测无关(数据题)
3 o# ~/ z0 b5 z1 d0 M/ a2020A炉温曲线无关(拟合题)/ f& g3 G3 F) V
近年物流配送 / 应急巡检 / 传感器网络TSP / VRP / 覆盖路径核心规律:只要赛题出现"一条路走遍多个地点",TSP 或其变体(VRP、TSP with Time Windows、多车 TSP)就是最自然的建模原型。先用 TSP 建原型,再根据约束升级为 VRP,是数模竞赛的成熟套路。 9. 常见误区与赛场自查# 误区 正解 8 x Z1 S( {5 V7 z' L) e0 O# L
1"TSP 可以用贪心算法精确求解"贪心(最近邻)不保证最优,存在反例9 P; [3 n2 I* s8 m0 G5 V
2"2-opt 一定得到全局最优"2-opt 是局部搜索,可能陷入局部最优$ E( P+ z g7 s& h
3"TSP 和指派问题等价"TSP 要求单一回路,指派问题允许子回路+ F0 }2 H s5 F- I7 L0 B' ~: G
4"子回路消除约束可以一次性写完"数量 级别,实际用逐步加入(lazy constraint); k( I1 C _- x3 q
5"DP 复杂度 "准确是 ,每状态需 转移
$ ` B5 G% q. T/ s6"所有 TSP 都有常数近似比"仅度量 TSP(三角不等式)有 Christofides 近似
' F( v7 v8 J/ P7 {9 T% [7"对称 TSP 和非对称 TSP 一样"对称: 种回路;非对称: 种赛场自查清单: - 我确认了距离矩阵是否对称、是否满足三角不等式(影响算法选择)。
- 我估计了可行解数量 ,判断暴力 / B&B / 启发式的适用性。
- 若用 B&B:下界函数是否安全(不高于最优值)且紧?
- 若用 2-opt:是否报告了与精确解的偏差(小规模对比)?
- 若用 Christofides:是否验证了三角不等式成立?
- 论文中是否报告了运行时间与最优性差距(gap)?
- 是否对结果做了敏感性分析(距离矩阵微调时回路是否稳定)?
- 是否给出了子回路消除的处理方式(lazy constraint 或逐步加入)?" S) N* C% R* p, y& _
10. 参考文献[1] Dantzig, G. B., Fulkerson, D. R., & Johnson, S. M. (1954). Solution of a Large-Scale Traveling-Salesman Problem. Operations Research, 2(4), 393–410. DOI: 10.1287/opre.2.4.393. https://doi.org/10.1287/opre.2.4.393 [2] Held, M., & Karp, R. M. (1961). A dynamic programming approach to sequencing problems. Proceedings of the 1961 16th ACM National Meeting, pp. 71.201–71.204. DOI: 10.1145/800029.808532. https://doi.org/10.1145/800029.808532 [3] Held, M., & Karp, R. M. (1962). The traveling-salesman problem and minimum spanning trees: Part II. Operations Research, 18(6), 1138–1162. DOI: 10.1287/opre.18.6.1138. [4] Lawler, E. L., Lenstra, J. K., Rinnooy Kan, A. H. G., & Shmoys, D. B. (Eds.) (1985). The Traveling Salesman Problem: A Guided Tour of Combinatorial Optimization. Wiley-Interscience Series in Discrete Mathematics, Vol. 12. Chichester: John Wiley & Sons. ISBN 978-0-471-90413-7. [5] Applegate, D. L., Bixby, R. E., Chvátal, V., & Cook, W. J. (2011). The Traveling Salesman Problem: A Computational Study. Princeton Series in Applied Mathematics. Princeton, NJ: Princeton University Press. ISBN 978-0-691-12993-8 (print), 978-1-400-84110-3 (ebook). https://press.princeton.edu/books/ebook/9781400841103/the-traveling-salesman-problem-pdf [6] Cook, W. (2012). In Pursuit of the Traveling Salesman: Mathematics at the Limits of Computation. Princeton, NJ: Princeton University Press. ISBN 978-0-691-16352-9. [7] 姜启源, 谢金星, 叶俊. 数学模型. 5版. 北京: 高等教育出版社, 2018. ISBN 978-7-04-049222-4. [8] 司守奎, 孙玺菁. 数学建模算法与应用. 3版. 北京: 国防工业出版社, 2021. ISBN 978-7-118-12278-7. [9] 胡运权, 主编. 运筹学教程. 5版. 北京: 清华大学出版社, 2018. ISBN 978-7-302-48125-6. https://www.tup.tsinghua.edu.cn/wap/tsxqy.aspx?id=07656604 给参赛学生的一句话:TSP 是数模优化题里"最简单的难题"——问题描述一句话,求解却需要一整套组合优化工具箱。小规模用 B&B/DP 保证最优,大规模用 2-opt 给出好解并报告 gap,这是评委最认可的"务实"策略。记住:能证明最优的解,比看似漂亮但无保证的解更有说服力。 ; V2 D0 q5 r* }5 l8 D
/ [4 Q7 e1 W$ O7 P C0 J* d |