1 条题解
-
0
题目题解
问题理解
给定一个简单无向连通图,从顶点 出发,每个顶点 的边按输入顺序编号为 到 。在时间 ,若位于顶点 ,必须执行以下操作之一:
- 等待一秒,
- 通过顶点 的第 条边移动到邻接点,耗时一秒。
求从顶点 到顶点 的最短总时间,以及在此最短总时间下的最少等待时间。
状态定义
设 表示在时间 到达顶点 所需的最少等待时间。初始状态为
状态转移
在时间 位于顶点 ,有以下两种转移:
-
等待一秒:
$$f(i, j+1) \leftarrow \min\big(f(i, j+1),\; f(i, j) + 1\big). $$
时间变为 ,等待时间增加 : -
移动:
$$f(k, j+1) \leftarrow \min\big(f(k, j+1),\; f(i, j)\big). $$
设 为顶点 的第 条边所连的顶点。则移动后时间变为 ,等待时间不变:
目标
设 为从 到 的最短总时间,则最小等待时间为
因此,只需对所有 计算 。
时间复杂度分析
若直接计算到时间 ,复杂度为 。
我们需要给出 的上界。
上界
引理:存在一条从 到 的路径,其顶点度数之和 。
证明:
- 取一条从 到 的最短路径(边数最少),设路径上有 个顶点。
- 由于路径是最短的,不存在连接路径上非相邻顶点的边(否则可缩短路径)。
- 也不存在路径外的一个顶点与路径上至少 个顶点相邻,因为那样同样可以缩短路径。
因此,路径上每个顶点的度数最多为 (与路径上前后顶点相邻)加上可能的路径外邻接点。
$$\sum_{u \in \text{path}} \deg(u) \le 2k + 3(n - k) = 3n - k. $$
由于每个路径外顶点最多与 个路径上顶点相邻,所以路径上顶点的度数之和满足:由于 ,有
最坏情况下的总时间
考虑这条路径,最坏策略是每次前进前等待到该顶点对应边出现。
对于路径上的顶点 ,最多等待 次(因为总有一时刻当前边就是要用的边)。
因此,总时间不超过路径上所有顶点的度数之和,即 。
算法复杂度
由于 ,我们可以计算所有 的状态。
状态数 ,每个状态转移 ,加上边信息预处理 ,总复杂度为对于 且 ,这是可行的。
算法步骤
- 读入图,计算每个顶点的度数及边的顺序。
- 初始化 ,其余 。
- 对 到 :
- 对于每个顶点 ,若 已知:
- 更新
- 设 为 的第 条边的另一端点,更新
- 对于每个顶点 ,若 已知:
- 找到最小的 使得 有限,输出 和 。
代码实现
#include <bits/stdc++.h> using namespace std; const int INF = 1e9; void solve() { int n, m; cin >> n >> m; vector<vector<int>> g(n); vector<int> deg(n, 0); for (int i = 0; i < m; i++) { int u, v; cin >> u >> v; u--; v--; g[u].push_back(v); g[v].push_back(u); deg[u]++; deg[v]++; } int max_time = 3 * n; // 上界 // f[i][j] = 在时间 j 到达顶点 i 的最少等待时间 vector<vector<int>> f(n, vector<int>(max_time + 1, INF)); f[0][0] = 0; for (int j = 0; j < max_time; j++) { for (int i = 0; i < n; i++) { if (f[i][j] == INF) continue; // 等待操作 f[i][j + 1] = min(f[i][j + 1], f[i][j] + 1); // 移动操作 int edge_idx = j % deg[i]; int to = g[i][edge_idx]; f[to][j + 1] = min(f[to][j + 1], f[i][j]); } } // 寻找答案 int best_time = INF, best_wait = INF; for (int j = 0; j <= max_time; j++) { if (f[n - 1][j] < best_time) { best_time = j; best_wait = f[n - 1][j]; } else if (f[n - 1][j] == best_time) { best_wait = min(best_wait, f[n - 1][j]); } } cout << best_time << " " << best_wait << "\n"; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
验证样例
样例 1:输出
4 2✓
样例 2:输出3 0✓
总结
本题的关键在于:
- 将问题转化为带时间维度的状态搜索。
- 利用最短路径的性质给出时间上界 ,从而将复杂度控制在 。
- 通过动态规划或 BFS 在有限时间范围内求解,并同时记录最少等待时间。
- 1
信息
- ID
- 6224
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者