1 条题解
-
0
「School Road」通学路题解
问题分析
我们需要判断是否存在一条从城市 到城市 的路径,满足:
- 长度大于最短距离
- 不重复经过任何城市(简单路径)
核心思路
这个问题可以通过图论和缩点技术来解决。关键观察是:如果存在多条不同的最短路径或者存在可以"绕路"的结构,那么就可能存在满足条件的路径。
算法详解
1. 计算最短路径
for (int i = 1; i <= n; i++) dis[i] = inf; priority_queue<pii> q; dis[1] = 0; q.push({0, 1}); while (!q.empty()) { int u = q.top().se; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (dis[v] > dis[u] + e[i].w) { dis[v] = dis[u] + e[i].w; q.push({-dis[v], v}); } } }使用Dijkstra算法计算从城市 到所有城市的最短距离。
2. 构建最短路径图并找点双连通分量
add_e(1, n, dis[n]), add_e(n, 1, dis[n]); tar(1);添加一条从 到 的虚拟边(长度为最短距离),然后使用Tarjan算法找点双连通分量。
void tar(int u) { dfn[u] = lw[u] = ++idx; st[++tp] = u; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (!dfn[v]) { tar(v); lw[u] = min(lw[u], lw[v]); if (lw[v] >= dfn[u]) { id[++scct].push_back(st[tp]); while (st[tp--] != v) { id[scct].push_back(st[tp]); } id[scct].push_back(u); } } else lw[u] = min(lw[u], dfn[v]); } }3. 识别关键的双连通分量
for (int i = 1; i <= scct; i++) { int tim = 0; for (int j : id[i]) tim += (j == 1) + (j == n); if (tim == 2) { for (int j : id[i]) bk[j] = 1; break; } }找到包含起点 和终点 的点双连通分量,这里面的节点构成了可能绕路的关键区域。
4. 构建简化图并进行缩点
for (int u = 1; u <= n; u++) { for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (bk[u] && bk[v] && u < v) add(u, v, e[i].w); } }在关键区域内构建简化图,然后进行度数-based的缩点:
queue<int> qq; for (int i = 2; i < n; i++) if (d[i] <= 2) qq.push(i); while (!qq.empty()) { int u = qq.front(); qq.pop(); if (!d[u]) continue; else if (d[u] == 1) { // 删除度数为1的节点(非起点终点) int v = (*f[u].begin()).fi; d[u]--, d[v]--; f[u].erase(v), f[v].erase(u); if (v != 1 && v != n && d[v] <= 2) qq.push(v); } else { // 合并度数为2的节点 int v1 = (*f[u].begin()).fi, v2 = (*--f[u].end()).fi; add(v1, v2, (f[u][v1] == -1 || f[u][v2] == -1) ? -1 : f[u][v1] + f[u][v2]); d[u] -= 2, d[v1]--, d[v2]--; f[u].erase(v1), f[v1].erase(u), f[u].erase(v2), f[v2].erase(u); if (v1 != 1 && v1 != n && d[v1] <= 2) qq.push(v1); if (v2 != 1 && v2 != n && d[v2] <= 2) qq.push(v2); } }5. 判断结果
if (f[1].size() > 1 || f[1][n] == -1) puts("1"); else puts("0");如果:
- 起点 有多个邻居(意味着存在选择)
- 或者 到 的边权为 (意味着存在多条不同长度的路径)
则输出 ,否则输出 。
算法正确性
为什么这样能判断是否存在绕路?
- 点双连通分量:包含 和 的点双意味着存在多条不相交的路径
- 度数缩点:将复杂网络简化为只保留关键结构
- 边权为-1:表示存在多条不同长度的路径,这正好对应绕路的可能性
时间复杂度
- Dijkstra:
- Tarjan:
- 缩点:
- 总复杂度:,满足数据范围要求
样例验证
样例1:输出0
- 所有路径长度都等于最短距离
- 没有绕路可能
样例2:输出1
- 存在长度为 的路径
- 满足绕路条件
样例4:输出1
- 额外的边 创造了绕路机会
总结
本题的巧妙之处在于:
- 问题转化:将绕路问题转化为图的结构性质问题
- 点双连通分量:识别包含起点终点的关键区域
- 度数-based缩点:简化图结构同时保留关键信息
- 边权标记:用 表示存在长度选择
这种"图结构分析 + 缩点简化"的方法在解决路径相关问题时非常有效,特别是当需要判断存在性而非具体构造时。
- 1
信息
- ID
- 3742
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 4
- 已通过
- 1
- 上传者