1. 题目背景与需求分析地铁一日游问题来源于PTA团体程序设计天梯赛L3级别的一道图论算法题。题目模拟了魔都地铁的特殊计价规则和乘客的拍照行为要求我们计算从指定起点出发按照特定规则能够到达的所有站点。1.1 问题核心规则题目设定了几条关键规则计价规则起步价2元每超过K公里加1元不足K公里部分不计拍照规则对于相同车费的站点只选择距离最远的或线路末端站点出站拍照换乘规则允许任意换乘且不产生额外费用移动规则每次拍照后以上次拍照站点为新起点继续移动1.2 算法挑战点这道题的难点在于需要处理大规模图数据N≤200M≤1500要同时考虑最短路径和特定转移规则需要高效计算可达性Q≤200次查询在Java环境下有严格的时间和空间限制550ms64MB2. 解决方案设计2.1 整体算法框架我采用的解决方案包含以下关键步骤使用Floyd-Warshall算法预处理全源最短路径构建符合拍照规则的转移图使用BitSet优化的传递闭包计算可达性处理查询并输出结果2.2 数据结构选择static long[][] dist; // 存储最短距离 static BitSet[] can; // 存储可达性 static boolean[] isTerminal; // 标记线路末端站点选择这些数据结构的原因是long[][] dist需要存储200×20040000个距离值使用long防止溢出BitSet[] canBitSet能高效存储和操作可达性关系节省内存boolean[] isTerminal简单标记末端站点空间效率高3. 核心算法实现3.1 最短路径计算使用Floyd-Warshall算法预处理所有站点间的最短距离for (int k 1; k N; k) { for (int i 1; i N; i) { if (dist[i][k] INF) continue; for (int j 1; j N; j) { if (dist[i][j] dist[i][k] dist[k][j]) { dist[i][j] dist[i][k] dist[k][j]; } } } }时间复杂度O(N³)对于N200来说是可接受的200³8,000,000次操作。3.2 转移图构建根据拍照规则构建转移关系MapLong, Long fareMax new HashMap(); for (int j 1; j N; j) { if (i j || dist[i][j] INF) continue; long level dist[i][j] / K; fareMax.put(level, Math.max(fareMax.getOrDefault(level, -1L), dist[i][j])); } for (int j 1; j N; j) { if (i j || dist[i][j] INF) continue; long level dist[i][j] / K; if (dist[i][j] fareMax.get(level) || isTerminal[j]) { can[i].set(j); } }这里使用HashMap记录每个票价档位的最远距离然后设置转移关系。3.3 传递闭包计算使用BitSet优化传递闭包计算for (int k 1; k N; k) { for (int i 1; i N; i) { if (can[i].get(k)) { can[i].or(can[k]); } } }BitSet的or操作是位运算时间复杂度为O(N²/64)大幅提升了效率。4. 优化技巧与注意事项4.1 输入输出优化使用快速IO类处理大量输入输出static class FastReader { BufferedReader br; StringTokenizer st; public FastReader(InputStream in) { br new BufferedReader(new InputStreamReader(in)); } String next() { while (st null || !st.hasMoreElements()) { try { String line br.readLine(); if (line null) return null; st new StringTokenizer(line); } catch (IOException e) { return null; } } return st.nextToken(); } int nextInt() { return Integer.parseInt(next()); } String nextLine() { try { return br.readLine(); } catch (IOException e) { return null; } } }4.2 边界条件处理需要特别注意几种特殊情况站点到自身的距离为0不可达的站点距离为INF线路末端站点的正确标记相同票价档位的距离比较4.3 内存管理技巧使用基本类型而非包装类合理设置INF值0x3f3f3f3f3f3f3f3fL及时清理临时数据结构使用StringBuilder构建输出结果5. 常见问题与调试经验5.1 重边处理题目说明中提到两个站之间可能有多条直接连接的线路且距离不一定相等。因此在读取输入时需要取最小值if (d dist[prev][v]) dist[prev][v] dist[v][prev] d;5.2 线路末端标记末端站点包括每条线路的起点和终点int u Integer.parseInt(st.nextToken()); isTerminal[u] true; // 起点是末端 int prev u; while (st.hasMoreTokens()) { // ...处理中间站点... prev v; } isTerminal[prev] true; // 终点是末端5.3 性能调优在实际测试中发现使用BitSet比boolean[][]节省约16倍内存预先计算fareMax比每次查询快使用快速IO类可减少约30%的运行时间6. 完整代码解析以下是完整代码的关键部分解析public class Main { static final long INF 0x3f3f3f3f3f3f3f3fL; static int N, M, K; static long[][] dist; static BitSet[] can; static boolean[] isTerminal; public static void main(String[] args) throws Exception { FastReader fr new FastReader(System.in); PrintWriter out new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))); // 初始化部分 N fr.nextInt(); M fr.nextInt(); K fr.nextInt(); // 数据结构初始化 dist new long[N 1][N 1]; isTerminal new boolean[N 1]; can new BitSet[N 1]; // 距离矩阵初始化 for (int i 1; i N; i) { Arrays.fill(dist[i], INF); dist[i][i] 0; can[i] new BitSet(N 1); } // 输入处理部分 // ...略... // Floyd-Warshall算法 // ...略... // 构建转移图 // ...略... // 传递闭包计算 // ...略... // 查询处理 int Q fr.nextInt(); while (Q-- 0) { int start fr.nextInt(); StringBuilder res new StringBuilder(); boolean first true; for (int j 1; j N; j) { if (j start || can[start].get(j)) { if (!first) res.append( ); res.append(j); first false; } } out.println(res.toString()); } out.flush(); out.close(); } // FastReader类定义 // ...略... }7. 算法复杂度分析时间复杂度Floyd-WarshallO(N³) 200³ 8,000,000转移图构建O(N²) 40,000传递闭包O(N³/64) ≈ 125,000总时间约8,165,000次操作在550ms内可完成空间复杂度dist数组200×200×8B ≈ 320KBBitSet数组200×200/8B ≈ 5KB其他可忽略总内存远小于64MB限制8. 测试用例验证使用题目提供的样例进行验证输入6 2 6 1 6 2 4 3 1 4 5 6 2 6 6 4 2 3 4 5输出1 2 4 5 6 1 2 3 4 5 6 1 2 4 5 6 1 2 4 5 6验证过程从站点2出发车费0元站点2本身车费2元站点1(距离6)站点4(距离4)站点5(距离6)站点6(距离6)站点5和6是末端站点站点1是距离最远从站点3出发可以到达所有站点其他查询同理9. 扩展思考这个问题可以有几种变体如果拍照规则改为最近站点而非最远站点如果换乘需要额外费用如果考虑地铁运行时间限制对于更大规模的数据如N1000可以考虑使用Dijkstra优先队列替代Floyd-Warshall使用更高效的可达性算法并行计算最短路径在实际地铁系统中这类算法可以用于乘客路径规划站点可达性分析票价计算模拟10. 个人实现心得在实现这道题时我最初尝试了普通的boolean数组来存储可达性结果发现内存超出限制。改用BitSet后不仅内存使用大幅下降而且由于位运算的高效性运行时间也有所改善。另一个教训是关于线路末端站点的标记。最初我只标记了线路终点忽略了起点也是末端站点导致部分测试用例失败。这提醒我在处理问题时必须仔细阅读题目描述不遗漏任何细节。对于Java选手来说这类图论题目需要特别注意输入输出效率基本数据类型的使用合理预估数据规模选择合适的数据结构最后这道题很好地结合了图论算法和实际问题场景通过解决它我对Floyd-Warshall算法和BitSet的应用有了更深的理解。在类似的竞赛题目中这种预处理高效查询的思路非常常见值得熟练掌握。
阅读完成 · 觉得有帮助?