codevs 2800 送外卖 floyd+状压DP 解题报告

题目描述 Description

有一个送外卖的,他手上有n份订单,他要把n份东西,分别送达n个不同的客户的手上。n个不同的客户分别在1~n个编号的城市中。送外卖的从0号城市出发,然后n个城市都要走一次(一个城市可以走多次),最后还要回到0点(他的单位),请问最短时间是多少。现在已知任意两个城市的直接通路的时间。

输入描述 Input Description

第一行一个正整数n (1<=n<=15)

接下来是一个(n+1)*(n+1)的矩阵,矩阵中的数均为不超过10000的正整数。矩阵的i行j列表示第i-1号城市和j-1号城市之间直接通路的时间。当然城市a到城市b的直接通路时间和城市b到城市a的直接通路时间不一定相同,也就是说道路都是单向的。

输出描述 Output Description

一个正整数表示最少花费的时间

样例输入 Sample Input

3
0 1 10 10
1 0 1 2
10 1 0 10
10 2 10 0

样例输出 Sample Output

8

数据范围及提示 Data Size & Hint

1<=n<=15

思路

题目用邻接矩阵给数据,可见数据范围的小。
这次出题人终于知道把数据范围写在该写的地方了。。。
既然每个点可以访问多次,那么我们考虑两点间的距离便只考虑最短距离,不考虑怎么到的方式和路径。选择Floyd 算法求出两点间的最短距离。非常的easy。

然后呢?
你看到15这种数据不心动吗?
你不会想要搜索吧。。。如果暴力搜索,答案将

### L2-043 龙龙外卖 Python 实现与解题思路 此问题的核心在于求解 **旅行商问题 (TSP)** 的变种,即从起点出发访问所有目标点至少一次并返回原点的最短路径距离。由于题目中的输入规模较小(最多 18 个点),因此可以采用缩动态规划来解决。 #### 动态规划的态定义 令 `dp[S][i]` 表示当前已经访问过的集合为 `S` (二进制表示哪些点已访问过),且最后到达的是第 `i` 个点时的最小代价。初始态下,只有起点被访问,其他点均未访问。 转移方程如下: \[ dp[S|{j}][j] = \min(dp[S|{j}][j], dp[S][i] + dist[i][j]) \] 其中 \( S|{j} \) 是将点 \( j \) 加入集合 \( S \),\( dist[i][j] \) 是从点 \( i \) 到点 \( j \) 的距离。 最终答案是从起点出发遍历所有点后再回到起点的最小总距离: \[ ans = \min_{i=1}^{n}(dp[(1<<n)-1][i] + dist[0][i]) \] 以下是完整的 Python 实现: ```python from itertools import combinations def solve(): n, m = map(int, input().split()) # 点数和边数 INF = float('inf') # 初始化邻接矩阵 dist = [[INF]*n for _ in range(n)] for i in range(n): dist[i][i] = 0 # 构建图 for _ in range(m): u, v = map(int, input().split()) dist[u][v] = dist[v][u] = 1 # Floyd-Warshall算法预处理任意两点间的最短路 for k in range(n): for i in range(n): for j in range(n): if dist[i][k] + dist[k][j] < dist[i][j]: dist[i][j] = dist[i][k] + dist[k][j] # 如果有不可达的情况,则无法完成任务 for i in range(1, n): if dist[0][i] == INF: print(-1) return # DP dp = [[INF]*(n) for _ in range(1 << n)] dp[1][0] = 0 # 起始态:仅访问了起点 for S in range(1 << n): # 遍历所有可能的态 for i in range(n): # 当前位于点i if not (S & (1 << i)): continue # 若点i不在集合S中则跳过 for j in range(n): # 尝试加入新的点j if S & (1 << j): continue # 若点j已经在集合S中则跳过 next_S = S | (1 << j) dp[next_S][j] = min(dp[next_S][j], dp[S][i] + dist[i][j]) # 计算最优解 res = INF all_visited = (1 << n) - 1 for i in range(1, n): res = min(res, dp[all_visited][i] + dist[i][0]) print(res) solve() ``` #### 关键点解析 1. 使用 **Floyd-Warshall** 算法预先计算每一对节点之间的最短路径[^2]。 2. 应用 **DP** 来枚举所有可能的子集以及对应的最短路径[^3]。 3. 对于每一个新加入的目标点,更新其对应的态值以确保全局最优解[^1]。 #### 时间复杂度分析 该方法的时间复杂度主要由两部分组成: - 图中最短路径的预处理阶段时间复杂度为 \( O(N^3) \)。 - 缩动态规划阶段时间复杂度为 \( O(2^N \cdot N^2) \)。 对于本题的数据范围 (\( N \leq 18 \)),上述算法能够高效运行。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值