1)题目意思
有 n 个村庄(1 号是总部),每个村庄坐标已知。直升机每次飞行的距离不能超过 D,但每到一个村庄都能加满油(所以每一段航程只要不超过 D 就合法)。
小蓝每个月从 1 号出发,要访问所有村庄至少一次(可以重复经过某些村庄,包括总部),最后回到 1 号。问最少总飞行距离是多少,输出保留 2 位小数。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2)思路解析(最短路预处理 + TSP 状压 DP)
关键观察 1:允许中途经过其它村庄/总部加油
从村 i 到村 j 不一定能直飞(距离可能 > D),但可以通过中转若干次。只要每一段不超过 D 就行。
因此我们先把图建成:
* 如果 dist(i,j) <= D,就有一条边,权重是欧氏距离
然后对这个图做 所有点对最短路(因为中转可能更短/更可行):
* 得到 g[i][j]:从 i 到 j 的最小可行飞行距离
* 如果 i 到 j 不可达,则 g[i][j]=INF
若有任意一个点从总部不可达,那么一定无法完成任务,输出 -1(题目没写这种情况,但数据可能保证可行;这里写上更稳)。
关键观察 2:访问所有点并回到起点 = TSP(可重复经过点,但最短路已吸收重复)
因为我们把“中转”都折算进 g[i][j] 里,所以路线只需要在 g 上做:
* 从 1 出发,访问每个点至少一次,回到 1
在最短路度量下,最优解等价于“每个点访问一次”的 TSP 状压 DP。
状态定义
用 mask 表示已访问集合(0..n-1 对应村 1..n):
* dp[mask][i]:从 0(总部)出发,访问了 mask 中的点,最后停在 i 的最小距离
初始化:
* dp[1<<0][0]=0
转移:
* dp[mask|(1<<j)][j] = min(dp[mask|(1<<j)][j], dp[mask][i] + g[i][j])
答案:
* min_i dp[ALL][i] + g[i][0]
复杂度:
* Floyd:O(n^3),n<=20 很小
* TSP:O(n^2 * 2^n),n<=20 可过
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3)代码